library(Seurat)
library(conos)
library(ggpubr)
library(tidyverse)
library(SingleCellExperiment)
library(chromVAR)
library(motifmatchr)
library(BSgenome.Hsapiens.UCSC.hg38)
# library(monocle3)
source("~/multiOmic_benchmark/utils.R")
source("~/multiOmic_benchmark/integrateBenchmark.R")
source("~/multiOmic_benchmark/preprocess/selectFeatures.R")

Based on the results of my benchmark, I set out to align expression and accessibility profiles from the F74 developing thymus dataset to detect changes in accessibility along pseudotime trajectories. While the benchmark was based on the task of label propagation, I here use the two most faithful methods (Seurat CCA and Conos) to achieve a common embedding of ATAC-seq and RNA-seq cells.

Load datasets.

seu.rna@assays$RNA@data
33694 x 8321 sparse Matrix of class "dgCMatrix"
   [[ suppressing 68 column names ‘AAACCTGAGTTCGATC_1’, ‘AAACCTGCAAGTTGTC_1’, ‘AAACCTGCAATACGCT_1’ ... ]]
   [[ suppressing 68 column names ‘AAACCTGAGTTCGATC_1’, ‘AAACCTGCAAGTTGTC_1’, ‘AAACCTGCAATACGCT_1’ ... ]]
                                                                                                                                                            
RP11-34P13.3  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......
FAM138A       . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......
OR4F5         . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......
RP11-34P13.7  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......
RP11-34P13.8  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......
RP11-34P13.14 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......
RP11-34P13.9  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......

 ..............................
 ........suppressing 8253 columns and 33680 rows in show(); maybe adjust 'options(max.print= *, width = *)'
 ..............................
   [[ suppressing 68 column names ‘AAACCTGAGTTCGATC_1’, ‘AAACCTGCAAGTTGTC_1’, ‘AAACCTGCAATACGCT_1’ ... ]]
                                                                                                                                                         
AC023491.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......
AC004556.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......
AC233755.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......
AC233755.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......
AC240274.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......
AC213203.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......
FAM231B    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......

Filter genes with zero variance

rna.sce <- rna.sce[which(rna.gene.var > 0),]
atac.sce <- atac.sce[which(atac.gene.var > 0),]

rna.sce; atac.sce
class: SingleCellExperiment 
dim: 24510 8321 
metadata(0):
assays(3): counts cpm logcounts
rownames(24510): RP11-34P13.3 RP11-34P13.7 ... AC233755.1 AC240274.1
rowData names(0):
colnames(8321): AAACCTGAGTTCGATC_1 AAACCTGCAAGTTGTC_1 ... TTTGTCAAGCTGAACG_2 TTTGTCAGTATTAGCC_2
colData names(1): annotation
reducedDimNames(0):
spikeNames(0):
class: SingleCellExperiment 
dim: 31122 5793 
metadata(0):
assays(3): counts cpm logcounts
rownames(31122): A1BG A1BG-AS1 ... ZYX ZZEF1
rowData names(0):
colnames(5793): AAACGAAAGTGAACCG-1 AAACGAACATCGGCCA-1 ... TTTGTGTTCGATCGCG-1 TTTGTGTTCTGAGTAC-1
colData names(28): orig.ident nCount_ATAC ... nFeature_ACTIVITY ident
reducedDimNames(2): LSI UMAP
spikeNames(0):

Integration of T cells clusters

I re-run the integration based on the T cell subset. To select cells from the scATAC dataset, I take the SnapATAC clusters that best correspond to T-cells, based on label transfer.

tcells.sce.atac <- atac.sce[,which(as.numeric(atac.sce$seurat_clusters) %in% c(1:9))]

tcells.rna.ix <- which(rna.sce$annotation %in% c("DN","DP (Q)", "DP (P)", "SP (1)", "SP (2)"))
tcells.sce.rna <- rna.sce[,tcells.rna.ix]

tcells.sce.list <- list(RNA=tcells.sce.rna, ATAC=tcells.sce.atac)

## Make color palette 4 cell types
cell.types <- as.character(unique(tcells.sce.rna$annotation))
cell.type.pal <- brewer.pal(length(cell.types), "Set1") %>% rev() %>% setNames(cell.types)

Next, I select genes on which to perform integration. I take the union of the most variable features in the RNA dataset and the most covered features in the ATAC dataset

Remove cell cycle genes, that might interfere with pseudotime ordering

cell_cycle_genes <- read.table("~/annotations/cell_cycle_genes.tsv")$V1

integrate_features_union <- union(hvg.rna, hcg.atac)
integrate_features_union <- setdiff(integrate_features_union, cell_cycle_genes) 

## Select features in both datasets
integrate_features_union <- intersect(integrate_features_union, intersect(rownames(tcells.sce.list$ATAC), rownames(tcells.sce.list$RNA))) 

Visualize T cells in RNA dataset

tcells.seu.list <- map(tcells.sce.list, ~ as.Seurat(.x))
All keys should be one or more alphanumeric characters followed by an underscore '_', setting key to LSI_All keys should be one or more alphanumeric characters followed by an underscore '_', setting key to UMAP_
tcells.RNA.union <- tcells.seu.list$RNA
VariableFeatures(tcells.RNA.union) <- integrate_features_union
tcells.RNA.union <- ScaleData(tcells.RNA.union) %>% RunPCA() %>% RunUMAP(dims=1:40)
Centering and scaling data matrix

  |                                                                                  
  |                                                                            |   0%
  |                                                                                  
  |===============                                                             |  20%
  |                                                                                  
  |==============================                                              |  40%
  |                                                                                  
  |==============================================                              |  60%
  |                                                                                  
  |=============================================================               |  80%
  |                                                                                  
  |============================================================================| 100%
The following 176 features requested have zero variance (running reduction without them): TM4SF18, MEOX2, ASGR2, DMRT2, KIR2DL4, CACNA1B, FREM2, MYZAP, TOX3, PRIMA1, PPP2R2B, PTPRZ1, CMTM5, TREM2, CALB2, TMTC1, CDH6, PKHD1L1, ISLR2, KRT7, MYH8, DCX, CCSER1, MSLN, CDH19, EYA4, P2RY12, PLA2G2D, ADAMTS16, KCNJ5, PAPPA, QRFPR, ADGRG2, NFIA-AS2, LUZP2, NALCN, FABP7, TSPEAR-AS1, GPR1, CD300LF, A4GALT, AP000439.1, CEACAM3, SLITRK2, KIR3DL1, AC147651.1, MUC15, FFAR4, C11orf53, KLK11, WIF1, CFHR1, TMEM233, FOLR3, GRID2, GALNT15, FAM19A1, TAC1, PTCHD4, CCL11, ZNF385B, GABRA1, ADH1B, LINC01048, LMOD1, TNNT1, ACTL6B, SEMA3E, HOXD-AS2, NTF4, SLC35F1, MANEAL, FEV, RAB3C, SYT9, HMCN2, ZFHX4-AS1, PCSK9, MMP12, ABCB11, AC002066.1, ADAM23, ADCY5, ADGB, ADGRB1, ANKRD29, ATP2B2, C4orf45, CCDC33, CDH18, CDK15, CERS1, CFAP161, CLRN1-AS1, CNBD1, CNGB3, CNTN5, COL8A2, CTNND2, DCC, DSCAM, EPHA6, FGF12, FSTL5, GABBR2, GABRB1, GABRG3, GALNT13, GHR, GLIS3, GNG12-AS1, GNGT1, GRIN2B, GRM1, GRM8, HMCN1, HPSE2, HS3ST5, HS6ST3, IL1RAPL2, ITGBL1, KCNB1, KCNH7, LINC00639, LINC01169, LINC01170, LINC01182, LINC01317, LINC01505, LINGO1, LINGO2, LRRC4C, LRRTM4, LY75-CD302, MAP6, MLIP, MYRIP, NEBL, NKAIN2, NKAIN3, NRG1, OPCML, PCDH15, PCDHGA4, PDZRN4, PIK3C2G, PKNOX2, PNPLA1, POU6F2, PPFIA2, PRR5-ARHGAP8, PTPN5, PTPRT, RALYL, RERG, RGS7, RIMS1, RIN2, RYR3, SHANK2, SNTG1, SORCS3, STON1-GTF2A1L, SYN2, TENM2, THSD4, TRPC6, TSPEAR, TVP23C-CDRT4, UNC80, USH1C, VAX2, VWA3B, XKR4, ZBTB7C, ZNF804BPC_ 1 
Positive:  SATB1, PTPRC, MTSS1, APBB1IP, LYST, SH2D1A, CAMK4, LBH, FBLN5, LEF1 
       PLEKHG1, VOPP1, CD247, MXD1, TCF12, ARHGEF7, ALDH1A2, NFATC3, TBC1D19, SYTL3 
       ADAMTS17, ANO6, DAPK1, CALN1, THEMIS, PITPNM2, NLGN4X, MAPRE2, GALNT7, ZNF280D 
Negative:  ENO1, GSTP1, FABP5, TMSB10, SMS, PKM, NME4, VIM, RPL37A, YWHAQ 
       NCL, IGFBP2, CAPG, NDUFA12, TRDC, PGK1, PARVB, LDHB, ATOX1, SELL 
       CDC123, NUDC, IGLL1, UBE2N, FXYD2, GMPS, C20orf27, SLC25A39, ANXA1, C12orf75 
PC_ 2 
Positive:  RPL37A, EIF3H, LDHB, TBCA, IL32, SERGEF, SMPD3, ITGAE, AATF, FBLN5 
       NDUFA12, SOD1, DNAJC15, C12orf75, MRPL33, TMSB10, HNRNPC, CCDC57, SMCO4, GDI2 
       OLA1, ALDH1A2, COX7A2L, CST3, CYSTM1, ATOX1, GYPC, PDCD6, FABP5, RALY 
Negative:  MBNL1, ITPR2, PTPRC, MTRNR2L12, ADAM10, MSI2, CDK6, SELL, JCHAIN, RNF213 
       MME, TCF12, BPTF, BCL11B, NIPBL, MACF1, PIK3R1, HIVEP3, SOCS2, GALNT7 
       IKZF2, RUNX1, SPTBN1, PRRC2B, BCL2, DIAPH1, MYCBP2, SLC38A1, MBP, RUFY3 
PC_ 3 
Positive:  HLA-B, TOX2, COTL1, CTSW, KLRB1, CD40LG, SIRPG, GZMM, CLDN1, CD74 
       ITM2A, GBP2, BACH2, HPGD, PDE4D, TNFRSF1B, S100A10, CLEC2D, XCL1, GFOD1 
       CRTAM, MATK, DENND2D, PDCD1, GIMAP4, STAT1, ZNF683, CD226, HLA-A, TNFRSF25 
Negative:  TFDP2, JCHAIN, ATP6AP1L, DEFA6, MME, GALNT2, PCGF5, ADGRG1, GLIPR1, MSI2 
       NINL, CEP70, CDK6, PTPN2, FXYD2, TRDC, GSTP1, SELL, SOCS2, RGPD3 
       SMPD3, FABP5, LYST, SSBP2, PITPNM2, DLEU7, UBE2E1, NUCB2, PPP1R1C, BCL11A 
PC_ 4 
Positive:  SMPD3, C12orf75, TUBA1C, LCP1, HIST1H2AB, SMS, SMCO4, GMPS, FABP5, XPO1 
       IGFBP2, CCND3, TAF15, RGS3, SREBF2, YWHAQ, TMSB15A, ABCD3, NCL, PHIP 
       GNAS, EPB41L2, SYNE2, STAG2, CNTLN, VIM, NUP210, MCTP1, NUDC, NFATC3 
Negative:  JCHAIN, SELL, DEFA6, SOCS2, GLIPR1, ATP6AP1L, MME, RGPD3, XG, DPP4 
       IFI6, ENAM, GNAL, EVL, NEGR1, FRMPD2, EVA1A, PIK3CD, GALNT2, NDST3 
       MBP, RNF144A, FXYD2, COL1A2, SMIM24, NDFIP2, OXNAD1, ACTN1, LSP1, LINC00861 
PC_ 5 
Positive:  HPGD, BACH2, TOX2, ITM2A, GZMM, ST6GAL1, ARAP2, CD96, TGFBR2, LZTFL1 
       VIM, EVL, CD44, SATB1, IL2, TUSC3, CYTIP, PDE4D, MAD1L1, SLC2A3 
       ARHGAP31, GPR183, PGK1, ANKRD44, STK4, BCL2, GNG2, IKZF1, ICOS, ETS1 
Negative:  XCL1, TNFRSF9, CTSW, XCL2, GBP2, TNFRSF1B, S100A4, GNG4, NPW, CD74 
       HOPX, IGFBP4, CD151, SH3BGRL2, NFATC1, LYST, ATP9A, NR4A2, LINC01480, CLEC2D 
       MIR3142HG, LINC01281, PREX1, HDAC9, PHACTR1, MAP7, CST3, CXCR3, PITPNM2, PDCD1 
09:42:53 UMAP embedding parameters a = 0.9922 b = 1.112
09:42:53 Read 7101 rows and found 40 numeric columns
09:42:53 Using Annoy for neighbor search, n_neighbors = 30
09:42:53 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
09:42:55 Writing NN index file to temp file /tmp/RtmpInlUFP/file4d1e59aaa998
09:42:55 Searching Annoy index using 1 thread, search_k = 3000
09:42:57 Annoy recall = 100%
09:42:59 Commencing smooth kNN distance calibration using 1 thread
09:43:03 Initializing from normalized Laplacian + noise
09:43:03 Commencing optimization for 500 epochs, with 314138 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
09:43:22 Optimization finished
DimPlot(tcells.RNA.union, group.by = "annotation", label=TRUE) + ggtitle("RNA - feature union")

Visualize markers

t.cell.markers <- list(known.markers = c("CD34", "IGLL1", "TRGC2", "TRDC", "PTCRA", "TRBC2", "TRAC", "CD4", "CD8A", "CD8B"),
                       chemokine.receptors = c("CCR9", "CCR7"),
                       tcr.activation = c("CD5", "CD27"),
                       proliferation=c("PCNA", "CDK1", "MKI67"),
                       cyclin.D = c("CCND2", "CCND3"),
                       recombination=c("RAG1", "RAG2"),
                       apoptosis=c("HRK","BMF", "TP53INP1"),
                       stage.markers = c("ST18", "HIVEP3", "RGPD3", "SMPD3", "AQP3", "RORC", "SATB1", "TOX2")
                       ) 
# FeaturePlot(tcells.RNA.ref, features = t.cell.markers$known.markers, cols = viridis::viridis(n=10))
FeaturePlot(tcells.RNA.union, features = t.cell.markers$known.markers, cols = viridis::viridis(n=10))

Visualize T cells in ATAC dataset: is the trajectory visible in the binary matrix?

tcells.ATAC.union <- tcells.seu.list$ATAC
# tcells.ATAC.union <- NormalizeData(tcells.ATAC.union)
VariableFeatures(tcells.ATAC.union) <- integrate_features_union
tcells.ATAC.union <- RunLSI(tcells.ATAC.union, n=50, scale.max = NULL)
RunLSI is being moved to Signac. Equivalent functionality can be achieved via the Signac::RunTFIDF and Signac::RunSVD functions; for more information on Signac, please see https://github.com/timoast/SignacRunLSI is being moved to Signac. Equivalent functionality can be achieved via the Signac::RunTFIDF and Signac::RunSVD functions; for more information on Signac, please see https://github.com/timoast/SignacRunLSI is being moved to Signac. Equivalent functionality can be achieved via the Signac::RunTFIDF and Signac::RunSVD functions; for more information on Signac, please see https://github.com/timoast/SignacPerforming TF-IDF normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Running SVD on TF-IDF matrix
Scaling cell embeddings
Cannot add objects with duplicate keys (offending key: LSI_), setting key to 'lsi_'
tcells.ATAC.union <- RunUMAP(tcells.ATAC.union, reduction = "lsi", dims = 1:50)
09:43:59 UMAP embedding parameters a = 0.9922 b = 1.112
09:43:59 Read 4977 rows and found 50 numeric columns
09:43:59 Using Annoy for neighbor search, n_neighbors = 30
09:43:59 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
09:44:00 Writing NN index file to temp file /tmp/RtmpInlUFP/file4d1e64c0f0d8
09:44:00 Searching Annoy index using 1 thread, search_k = 3000
09:44:02 Annoy recall = 100%
09:44:04 Commencing smooth kNN distance calibration using 1 thread
09:44:08 Initializing from normalized Laplacian + noise
09:44:08 Commencing optimization for 500 epochs, with 172060 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
09:44:21 Optimization finished
Cannot add objects with duplicate keys (offending key: UMAP_), setting key to 'umap_'
DimPlot(tcells.ATAC.union, reduction = "umap", group.by = "seurat_clusters", label = TRUE) + ggtitle("ATAC gmat")

Run CCA and Conos

Run CCA

sce.list <- tcells.sce.list
reference = "RNA"
query = "ATAC" 
seurat.list <- imap(sce.list, ~ as.Seurat(.x, assay=.y))
All keys should be one or more alphanumeric characters followed by an underscore '_', setting key to LSI_All keys should be one or more alphanumeric characters followed by an underscore '_', setting key to UMAP_
seurat.list <- imap(seurat.list, ~ RenameCells(.x, add.cell.id=.y))
## Scale data
seurat.list <- map(seurat.list, ~ ScaleData(.x))
Centering and scaling data matrix

  |                                                                                                                                                     
  |                                                                                                                                               |   0%
  |                                                                                                                                                     
  |======                                                                                                                                         |   4%
  |                                                                                                                                                     
  |===========                                                                                                                                    |   8%
  |                                                                                                                                                     
  |=================                                                                                                                              |  12%
  |                                                                                                                                                     
  |=======================                                                                                                                        |  16%
  |                                                                                                                                                     
  |=============================                                                                                                                  |  20%
  |                                                                                                                                                     
  |==================================                                                                                                             |  24%
  |                                                                                                                                                     
  |========================================                                                                                                       |  28%
  |                                                                                                                                                     
  |==============================================                                                                                                 |  32%
  |                                                                                                                                                     
  |===================================================                                                                                            |  36%
  |                                                                                                                                                     
  |=========================================================                                                                                      |  40%
  |                                                                                                                                                     
  |===============================================================                                                                                |  44%
  |                                                                                                                                                     
  |=====================================================================                                                                          |  48%
  |                                                                                                                                                     
  |==========================================================================                                                                     |  52%
  |                                                                                                                                                     
  |================================================================================                                                               |  56%
  |                                                                                                                                                     
  |======================================================================================                                                         |  60%
  |                                                                                                                                                     
  |============================================================================================                                                   |  64%
  |                                                                                                                                                     
  |=================================================================================================                                              |  68%
  |                                                                                                                                                     
  |=======================================================================================================                                        |  72%
  |                                                                                                                                                     
  |=============================================================================================================                                  |  76%
  |                                                                                                                                                     
  |==================================================================================================================                             |  80%
  |                                                                                                                                                     
  |========================================================================================================================                       |  84%
  |                                                                                                                                                     
  |==============================================================================================================================                 |  88%
  |                                                                                                                                                     
  |====================================================================================================================================           |  92%
  |                                                                                                                                                     
  |=========================================================================================================================================      |  96%
  |                                                                                                                                                     
  |===============================================================================================================================================| 100%
Centering and scaling data matrix

  |                                                                                                                                                     
  |                                                                                                                                               |   0%
  |                                                                                                                                                     
  |====                                                                                                                                           |   3%
  |                                                                                                                                                     
  |=========                                                                                                                                      |   6%
  |                                                                                                                                                     
  |=============                                                                                                                                  |   9%
  |                                                                                                                                                     
  |==================                                                                                                                             |  12%
  |                                                                                                                                                     
  |======================                                                                                                                         |  16%
  |                                                                                                                                                     
  |===========================                                                                                                                    |  19%
  |                                                                                                                                                     
  |===============================                                                                                                                |  22%
  |                                                                                                                                                     
  |====================================                                                                                                           |  25%
  |                                                                                                                                                     
  |========================================                                                                                                       |  28%
  |                                                                                                                                                     
  |=============================================                                                                                                  |  31%
  |                                                                                                                                                     
  |=================================================                                                                                              |  34%
  |                                                                                                                                                     
  |======================================================                                                                                         |  38%
  |                                                                                                                                                     
  |==========================================================                                                                                     |  41%
  |                                                                                                                                                     
  |===============================================================                                                                                |  44%
  |                                                                                                                                                     
  |===================================================================                                                                            |  47%
  |                                                                                                                                                     
  |========================================================================                                                                       |  50%
  |                                                                                                                                                     
  |============================================================================                                                                   |  53%
  |                                                                                                                                                     
  |================================================================================                                                               |  56%
  |                                                                                                                                                     
  |=====================================================================================                                                          |  59%
  |                                                                                                                                                     
  |=========================================================================================                                                      |  62%
  |                                                                                                                                                     
  |==============================================================================================                                                 |  66%
  |                                                                                                                                                     
  |==================================================================================================                                             |  69%
  |                                                                                                                                                     
  |=======================================================================================================                                        |  72%
  |                                                                                                                                                     
  |===========================================================================================================                                    |  75%
  |                                                                                                                                                     
  |================================================================================================================                               |  78%
  |                                                                                                                                                     
  |====================================================================================================================                           |  81%
  |                                                                                                                                                     
  |=========================================================================================================================                      |  84%
  |                                                                                                                                                     
  |=============================================================================================================================                  |  88%
  |                                                                                                                                                     
  |==================================================================================================================================             |  91%
  |                                                                                                                                                     
  |======================================================================================================================================         |  94%
  |                                                                                                                                                     
  |===========================================================================================================================================    |  97%
  |                                                                                                                                                     
  |===============================================================================================================================================| 100%
## Calculate CCA anchors
transfer.anchors <- FindTransferAnchors(reference = seurat.list[[reference]], 
                                        query = seurat.list[[query]],
                                        features = integrate_features_union, 
                                        reduction = "cca")
Running CCA on different assaysRunning CCA
Merging objects
Finding neighborhoods
Finding anchors
    Found 19433 anchors
Filtering anchors
    Retained 1616 anchors
Extracting within-dataset neighbors
## Impute expression profiles for ATAC cells (for all genes, not just integration features)
refdata <- GetAssayData(seurat.list$RNA, assay = "RNA", slot = "data")
imputation <- TransferData(anchorset = transfer.anchors, refdata = refdata, weight.reduction = seurat.list$ATAC[["LSI"]])
Finding integration vectors
Finding integration vector weights
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Transfering 24510 features onto reference data
## Merge datasets and co-embed
seurat.list$ATAC[["RNA"]] <- imputation
coembed <- merge(x = seurat.list$RNA, y = seurat.list$ATAC)

coembed <- ScaleData(coembed, features = integrate_features_union, do.scale = FALSE)
Centering data matrix

  |                                                                                                                                                     
  |                                                                                                                                               |   0%
  |                                                                                                                                                     
  |=============================                                                                                                                  |  20%
  |                                                                                                                                                     
  |=========================================================                                                                                      |  40%
  |                                                                                                                                                     
  |======================================================================================                                                         |  60%
  |                                                                                                                                                     
  |==================================================================================================================                             |  80%
  |                                                                                                                                                     
  |===============================================================================================================================================| 100%
coembed <- RunPCA(coembed, features = integrate_features_union, verbose = FALSE)
coembed <- RunUMAP(coembed, dims = 1:30)
09:52:05 UMAP embedding parameters a = 0.9922 b = 1.112
09:52:05 Read 12078 rows and found 30 numeric columns
09:52:05 Using Annoy for neighbor search, n_neighbors = 30
09:52:05 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
09:52:07 Writing NN index file to temp file /tmp/RtmpInlUFP/file4d1e3a771214
09:52:07 Searching Annoy index using 1 thread, search_k = 3000
09:52:12 Annoy recall = 100%
09:52:15 Commencing smooth kNN distance calibration using 1 thread
09:52:19 Initializing from normalized Laplacian + noise
09:52:19 Commencing optimization for 200 epochs, with 559810 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
09:52:33 Optimization finished
coembed <- AddMetaData(coembed, metadata = ifelse(colnames(coembed) %in% colnames(seurat.list[[reference]]), reference, query), col.name = "tech")

DimPlot(coembed, group.by = c('tech', "annotation"))

FeaturePlot(coembed, features = t.cell.markers$stage.markers, split.by = "tech", cols = viridis::viridis(n=100)) + ggtitle("Stage Markers")

FeaturePlot(coembed, features = t.cell.markers$known.markers, split.by = "tech", slot = "data", cols = viridis::viridis(n=100))

FeaturePlot(coembed, features = t.cell.markers$recombination, split.by = "tech", slot = "data", cols = viridis::viridis(n=100)) 

Transfer labels on ATAC dataset

celltype.predictions <- TransferData(anchorset = transfer.anchors, 
                                     refdata = seurat.list[[reference]]$annotation, 
                                     weight.reduction = seurat.list$ATAC[["LSI"]])
Finding integration vectors
Finding integration vector weights
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Predicting cell labels
coembed <- AddMetaData(coembed, metadata = celltype.predictions)
coembed@meta.data %<>%
  rownames_to_column() %>%
  dplyr::mutate(annotation=ifelse(is.na(predicted.id) , annotation, NA)) %>%
  column_to_rownames()

coembed@meta.data <-
  coembed@meta.data %>%
  rownames_to_column() %>%
  dplyr::mutate(annotation=ifelse(is.na(annotation) & prediction.score.max > 0.5, predicted.id, annotation)) %>%
  column_to_rownames()
CombinePlots(
  list(DimPlot(coembed, group.by = c("predicted.id"), cols = cell.type.pal) + ggtitle("prediction"),
  DimPlot(coembed, group.by = c("annotation"), cols = cell.type.pal) + ggtitle("Original + prediction")),
  legend = "top"
  )

FeaturePlot(coembed, features = "prediction.score.max", cells = which(coembed$tech=="ATAC")) + scale_color_viridis_c()
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.

Run Pseudotime analysis

Identify cell of origin based on IGLL1 and CD34

FeaturePlot(coembed, features = c("IGLL1", "CD34"), split.by = "tech", slot = "data", cols = viridis::viridis(n=100))

cell.oo <-
  coembed@meta.data %>% 
  rownames_to_column("cell") %>%
  mutate(IGLL1=coembed@assays$RNA@counts["IGLL1",cell]) %>%
  select(cell, annotation, IGLL1) %>%
  arrange(-IGLL1) %>%
  filter(annotation=="DN") %>%
  top_n(1, IGLL1) %>%
  pull(cell)

coembed@reductions$umap@cell.embeddings %>%
  as.tibble(rownames="cell") %>%
  mutate(cell.oo = ifelse(cell %in% cell.oo, T, F)) %>%
  ggplot(aes(UMAP_1, UMAP_2)) +
  geom_point(color="grey50") +
  geom_point(data=. %>% filter(cell.oo),color='red') +
  ggrepel::geom_text_repel(data=. %>% filter(cell.oo), aes(label="cell of origin"), color='red') +
  theme_cowplot() 
`as.tibble()` is deprecated, use `as_tibble()` (but mind the new semantics).
This warning is displayed once per session.

coembed <- AddMetaData(coembed, ifelse(colnames(coembed)==cell.oo, TRUE, FALSE), col.name = "iroot_cell")

  

I infer pseudotime using the diffusion pseudotime algorithm as implemented in scanpy. Making an R/reticulate wrapper for this function would be nice, but for now, see multiOmic_benchmark/DPT_tcells.ipynb.

Read scanpy output and save in R object.

dpt <- read.csv('~/my_data/Tcells_CCA_integration_20191127_scanpy_dpt.csv') %>%
  select(X, dpt_pseudotime)

coembed <- AddMetaData(coembed, column_to_rownames(dpt, 'X'))
saveRDS(coembed, "~/my_data/Tcells_CCA_integration_seurat_20191127.Rmd")
coembed <- readRDS("~/my_data/Tcells_CCA_integration_seurat_20191127.Rmd")

Visualize pseudotime

Save figure

Check expression of markers along pseudotime

coembed@assays$RNA@data[t.cell.markers$known.markers, ] %>%
  as.matrix() %>%
  reshape2::melt(varnames=c("gene", "cell")) %>%
  group_by(gene) %>%
  # mutate(value=(value-mean(value))/sd(value)) %>%
  left_join(coembed@meta.data[,"dpt_pseudotime", drop=F] %>% rownames_to_column("cell")) %>%
  mutate(pseudotime.rank=dense_rank(dpt_pseudotime)) %>%
  ggplot(aes(pseudotime.rank, gene)) +
  geom_tile(aes(fill=value)) +
  scale_fill_viridis_c()

Bin pseudotime and visualize cell type composition

Correlation between global accessibility and pseudotime ordering.

Motif analysis

I initially wanted to call peaks from SnapATAC clusters, then build a cell x peak matrix on those detected peaks, but SnapATAC/MACS2 were drivin me nuts.

Alternative: load peak matrix from cellranger and add to snap object

snap.pmat <- createSnapFromPmat(mat=t(filt.peaks[,snap.out@barcode]), barcodes=snap.out@barcode, peaks=peaks.gr)
snap.pmat
number of barcodes: 5793
number of bins: 0
number of genes: 0
number of peaks: 93607
number of motifs: 0

Calculating deviations in TF accessibility using ChromVAR. This is a measure of how much is motif accessibility in each cell is enriched compared to all the cells and general cell coverage. While SnapATAC has an wrapper around ChromVAR that outputs the deviation matrix, I just take the code from that function and run every step separately to keep the useful outputs and statistics of chromVAR.

snap.pmat = makeBinary(snap.pmat, "pmat")

obj = snap.pmat
input.mat="pmat"
min.count=10
species="Homo sapiens"
genome=BSgenome.Hsapiens.UCSC.hg38

data.use = obj@pmat
peak.use = obj@peak

ncell = nrow(data.use)

idy = which(Matrix::colSums(data.use) >= min.count)
data.use = data.use[,idy,dropping=TRUE]
    
peak.use = peak.use[idy]

rse <- SummarizedExperiment(
        assays = list(counts = t(data.use)), 
                 rowRanges = peak.use, 
                 colData = DataFrame(Cell_Type=1:nrow(data.use), depth=Matrix::rowSums(data.use))
    );
rse <- addGCBias(rse, genome = genome);
motifs <- getJasparMotifs(collection = "CORE", species=species);
motif_mm <- matchMotifs(motifs, rse, genome = genome);
dev <- computeDeviations(object = rse, annotations = motif_mm);
'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading
var <- computeVariability(dev)
'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading'package:stats' may not be available when loading
plotVariability(var, use_plotly = F, n = 8)

Save

rowData(dev) %<>%
  as.tibble(rownames="motif") %>%
  full_join(var) %>%
  column_to_rownames('motif') %>%
  DataFrame()
Joining, by = "name"
Column `name` joining character vector and factor, coercing into character vector

Visualize motif enrichment

Compare motif accessibility and TF gene expression

genex.df <- coembed@assays$RNA@data[tf.topvar[which(tf.topvar %in% rownames(coembed@assays$RNA))],] %>%
  as.matrix() %>%
  reshape2::melt(varnames=c("gene", 'cell')) %>%
  filter(str_detect(cell, "RNA_")) %>%
  left_join(dpt.df) %>%
  arrange(dpt_bin) %>%
  mutate(tech="RNA")
Joining, by = "cell"
Column `cell` joining factor and character vector, coercing into character vector
bind_rows(access.df, genex.df) %>%
  filter(gene %in% c("SPI1", "RUNX2","RUNX3", 'TCF7L2', "E2F4")) %>%
    filter(!str_detect(cell, "^ATAC_") | tech=="ATAC") %>%
  # group_by(tech, gene) %>%
  # mutate(value=scale(value)) %>%
  # ungroup() %>%
  # drop_na() %>%
# filter(tech=="RNA") %>%
  drop_na(dpt_bin) %>%
  ggplot(aes(dpt_bin, value)) +
  geom_point(aes(color=annotation), alpha=0.2) +
  facet_grid(tech~gene, scales = "free_y") +
  geom_smooth()
binding character and factor vector, coercing into character vector

bind_rows(access.df, genex.df) %>%
  filter(gene %in% c("ELK3", "JUNB", "FOS")) %>%
  filter(!str_detect(cell, "^ATAC_") | tech=="ATAC") %>%
  # group_by(tech, gene) %>%
  # mutate(value=scale(value)) %>%
  # ungroup() %>%
  drop_na(dpt_bin) %>%
  ggplot(aes(dpt_bin, value)) +
  geom_point(aes(color=annotation), alpha=0.2) +
  facet_grid(tech~gene, scales = "free_y") +
  geom_smooth() +
  scale_color_manual(values=cell.type.pal)
binding character and factor vector, coercing into character vector

Pseudotime lag between DP(Q) in accessibility and gene expression

The DP (Q) cluster in the ATAC cells is scored with high confidence

DimPlot(cca.obj, group.by=c("annotation","tech"), reduction = "cca", dims = 1:2)
DimPlot(cca.obj, group.by=c("annotation","tech"), reduction = "cca", dims = 3:4)

DimPlot(cca.obj, group.by=c("annotation","tech"), reduction = "cca", dims = 5:6)

plot_grid(
  DimPlot(dpq.coembed, group.by = c("tech"), reduction = "pca") ,
  FeaturePlot(dpq.coembed, feature="dpt_pseudotime", reduction = "pca") + scale_color_viridis_c() 
  )
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.

FeaturePlot(dpq.coembed, feature=unique(topPC1.df$label)[1:6], reduction = "umap") 
The following requested variables were not found: NA

FeaturePlot(dpq.coembed, feature="FABP5", reduction = "pca", slot = "raw.data") 
Error in slot(object = object, name = slot) : 
  no slot of name "raw.data" for this object of class "Assay"


LS0tCnRpdGxlOiAiUHNldWRvdGltZSBhbmFseXNpcyBvZiBULWNlbGxzIGluIGRldmVsb3BpbmcgdGh5bXVzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7cn0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoY29ub3MpCmxpYnJhcnkoZ2dwdWJyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShTaW5nbGVDZWxsRXhwZXJpbWVudCkKbGlicmFyeShjaHJvbVZBUikKbGlicmFyeShtb3RpZm1hdGNocikKbGlicmFyeShCU2dlbm9tZS5Ic2FwaWVucy5VQ1NDLmhnMzgpCiMgbGlicmFyeShtb25vY2xlMykKc291cmNlKCJ+L211bHRpT21pY19iZW5jaG1hcmsvdXRpbHMuUiIpCnNvdXJjZSgifi9tdWx0aU9taWNfYmVuY2htYXJrL2ludGVncmF0ZUJlbmNobWFyay5SIikKc291cmNlKCJ+L211bHRpT21pY19iZW5jaG1hcmsvcHJlcHJvY2Vzcy9zZWxlY3RGZWF0dXJlcy5SIikKYGBgCgpCYXNlZCBvbiB0aGUgcmVzdWx0cyBvZiBteSBiZW5jaG1hcmssIEkgc2V0IG91dCB0byBhbGlnbiBleHByZXNzaW9uIGFuZCBhY2Nlc3NpYmlsaXR5IHByb2ZpbGVzIGZyb20gdGhlIEY3NCBkZXZlbG9waW5nIHRoeW11cyBkYXRhc2V0IHRvIGRldGVjdCBjaGFuZ2VzIGluIGFjY2Vzc2liaWxpdHkgYWxvbmcgcHNldWRvdGltZSB0cmFqZWN0b3JpZXMuIFdoaWxlIHRoZSBiZW5jaG1hcmsgd2FzIGJhc2VkIG9uIHRoZSB0YXNrIG9mIGxhYmVsIHByb3BhZ2F0aW9uLCBJIGhlcmUgdXNlIHRoZSB0d28gbW9zdCBmYWl0aGZ1bCBtZXRob2RzIChTZXVyYXQgQ0NBIGFuZCBDb25vcykgdG8gYWNoaWV2ZSBhIGNvbW1vbiBlbWJlZGRpbmcgb2YgQVRBQy1zZXEgYW5kIFJOQS1zZXEgY2VsbHMuCgpMb2FkIGRhdGFzZXRzLgoKYGBge3J9CnJuYS5zY2UgPC0gcmVhZFJEUygifi9teV9kYXRhL0Y3NF9STkFfc2V1cmF0X3Byb2Nlc3NlZC5SRFMiKQphdGFjLnNjZSA8LSByZWFkUkRTKCJ+L215X2RhdGEvRjc0X0FUQUNfc25hcEF0YWNfcHJvY2Vzc2VkX2JnbWF0LlJEUyIpCgojIyBSZS1ub3JtYWxpemUgUk5BIGRhdGEKc2V1LnJuYSA8LSBhcy5TZXVyYXQocm5hLnNjZSwgY291bnRzID0gImNvdW50cyIpCnNldS5ybmEgPC0gTm9ybWFsaXplRGF0YShzZXUucm5hKQpsb2djb3VudHMocm5hLnNjZSkgPC0gc2V1LnJuYUBhc3NheXMkUk5BQGRhdGEKCmBgYAoKRmlsdGVyIGdlbmVzIHdpdGggemVybyB2YXJpYW5jZQpgYGB7cn0Kcm5hLmdlbmUudmFyIDwtIGFzLm1hdHJpeChjb3VudHMocm5hLnNjZSkpICU+JSByb3dWYXJzKCkKYXRhYy5nZW5lLnZhciA8LSBhcy5tYXRyaXgoY291bnRzKGF0YWMuc2NlKSkgJT4lIHJvd1ZhcnMoKQoKcm5hLnNjZSA8LSBybmEuc2NlW3doaWNoKHJuYS5nZW5lLnZhciA+IDApLF0KYXRhYy5zY2UgPC0gYXRhYy5zY2Vbd2hpY2goYXRhYy5nZW5lLnZhciA+IDApLF0KCnJuYS5zY2U7IGF0YWMuc2NlCmBgYAoKCiMjIEludGVncmF0aW9uIG9mIFQgY2VsbHMgY2x1c3RlcnMKSSByZS1ydW4gdGhlIGludGVncmF0aW9uIGJhc2VkIG9uIHRoZSBUIGNlbGwgc3Vic2V0LiBUbyBzZWxlY3QgY2VsbHMgZnJvbSB0aGUgc2NBVEFDIGRhdGFzZXQsIEkgdGFrZSB0aGUgU25hcEFUQUMgY2x1c3RlcnMgdGhhdCBiZXN0IGNvcnJlc3BvbmQgdG8gVC1jZWxscywgYmFzZWQgb24gbGFiZWwgdHJhbnNmZXIuCgpgYGB7cn0KdGNlbGxzLnNjZS5hdGFjIDwtIGF0YWMuc2NlWyx3aGljaChhcy5udW1lcmljKGF0YWMuc2NlJHNldXJhdF9jbHVzdGVycykgJWluJSBjKDE6OSkpXQoKdGNlbGxzLnJuYS5peCA8LSB3aGljaChybmEuc2NlJGFubm90YXRpb24gJWluJSBjKCJETiIsIkRQIChRKSIsICJEUCAoUCkiLCAiU1AgKDEpIiwgIlNQICgyKSIpKQp0Y2VsbHMuc2NlLnJuYSA8LSBybmEuc2NlWyx0Y2VsbHMucm5hLml4XQoKdGNlbGxzLnNjZS5saXN0IDwtIGxpc3QoUk5BPXRjZWxscy5zY2Uucm5hLCBBVEFDPXRjZWxscy5zY2UuYXRhYykKCiMjIE1ha2UgY29sb3IgcGFsZXR0ZSA0IGNlbGwgdHlwZXMKY2VsbC50eXBlcyA8LSBhcy5jaGFyYWN0ZXIodW5pcXVlKHRjZWxscy5zY2Uucm5hJGFubm90YXRpb24pKQpjZWxsLnR5cGUucGFsIDwtIGJyZXdlci5wYWwobGVuZ3RoKGNlbGwudHlwZXMpLCAiU2V0MSIpICU+JSByZXYoKSAlPiUgc2V0TmFtZXMoY2VsbC50eXBlcykKYGBgCgpOZXh0LCBJIHNlbGVjdCBnZW5lcyBvbiB3aGljaCB0byBwZXJmb3JtIGludGVncmF0aW9uLiBJIHRha2UgdGhlIHVuaW9uIG9mIHRoZSBtb3N0IHZhcmlhYmxlIGZlYXR1cmVzIGluIHRoZSBSTkEgZGF0YXNldCBhbmQgdGhlIG1vc3QgY292ZXJlZCBmZWF0dXJlcyBpbiB0aGUgQVRBQyBkYXRhc2V0CgpgYGB7cn0KaGNnLmF0YWMgPC0gc2VsZWN0X2hpZ2hseUNvdmVyZWQodGNlbGxzLnNjZS5saXN0JEFUQUMsIGZyYWNfY2VsbHMgPSAwLjIpCmh2Zy5ybmEgPC0gc2VsZWN0X2hpZ2hseVZhcmlhYmxlKHRjZWxscy5zY2UubGlzdCRSTkEpCgpzZXUucm5hIDwtIEZpbmRWYXJpYWJsZUZlYXR1cmVzKHNldS5ybmEsIG5mZWF0dXJlcyA9IDIwMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBzZWxlY3Rpb24ubWV0aG9kID0gIm12cCIsIGRpc3BlcnNpb24uY3V0b2ZmPWMoMC43LCAxMDApLCBtZWFuLmN1dG9mZj1jKDAuMDIsIDMpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQpodmcucm5hIDwtIFZhcmlhYmxlRmVhdHVyZXMoc2V1LnJuYSkKClZhcmlhYmxlRmVhdHVyZVBsb3Qoc2V1LnJuYSkKVXBTZXRSOjp1cHNldChVcFNldFI6OmZyb21MaXN0KGxpc3QoSFZHLlJOQT1odmcucm5hLCBIQ0cuQVRBQz1oY2cuYXRhYykpKQpgYGAKClJlbW92ZSBjZWxsIGN5Y2xlIGdlbmVzLCB0aGF0IG1pZ2h0IGludGVyZmVyZSB3aXRoIHBzZXVkb3RpbWUgb3JkZXJpbmcKYGBge3J9CmNlbGxfY3ljbGVfZ2VuZXMgPC0gcmVhZC50YWJsZSgifi9hbm5vdGF0aW9ucy9jZWxsX2N5Y2xlX2dlbmVzLnRzdiIpJFYxCgppbnRlZ3JhdGVfZmVhdHVyZXNfdW5pb24gPC0gdW5pb24oaHZnLnJuYSwgaGNnLmF0YWMpCmludGVncmF0ZV9mZWF0dXJlc191bmlvbiA8LSBzZXRkaWZmKGludGVncmF0ZV9mZWF0dXJlc191bmlvbiwgY2VsbF9jeWNsZV9nZW5lcykgCgojIyBTZWxlY3QgZmVhdHVyZXMgaW4gYm90aCBkYXRhc2V0cwppbnRlZ3JhdGVfZmVhdHVyZXNfdW5pb24gPC0gaW50ZXJzZWN0KGludGVncmF0ZV9mZWF0dXJlc191bmlvbiwgaW50ZXJzZWN0KHJvd25hbWVzKHRjZWxscy5zY2UubGlzdCRBVEFDKSwgcm93bmFtZXModGNlbGxzLnNjZS5saXN0JFJOQSkpKSAKCmBgYAoKVmlzdWFsaXplIFQgY2VsbHMgaW4gUk5BIGRhdGFzZXQKYGBge3J9CnRjZWxscy5zZXUubGlzdCA8LSBtYXAodGNlbGxzLnNjZS5saXN0LCB+IGFzLlNldXJhdCgueCkpCnRjZWxscy5STkEudW5pb24gPC0gdGNlbGxzLnNldS5saXN0JFJOQQpWYXJpYWJsZUZlYXR1cmVzKHRjZWxscy5STkEudW5pb24pIDwtIGludGVncmF0ZV9mZWF0dXJlc191bmlvbgp0Y2VsbHMuUk5BLnVuaW9uIDwtIFNjYWxlRGF0YSh0Y2VsbHMuUk5BLnVuaW9uKSAlPiUgUnVuUENBKCkgJT4lIFJ1blVNQVAoZGltcz0xOjQwKQoKRGltUGxvdCh0Y2VsbHMuUk5BLnVuaW9uLCBncm91cC5ieSA9ICJhbm5vdGF0aW9uIiwgbGFiZWw9VFJVRSkgKyBnZ3RpdGxlKCJSTkEgLSBmZWF0dXJlIHVuaW9uIikKYGBgCgpWaXN1YWxpemUgbWFya2VycyAKYGBge3IsIGZpZy53aWR0aD0xNSwgZmlnLmhlaWdodD0xMH0KdC5jZWxsLm1hcmtlcnMgPC0gbGlzdChrbm93bi5tYXJrZXJzID0gYygiQ0QzNCIsICJJR0xMMSIsICJUUkdDMiIsICJUUkRDIiwgIlBUQ1JBIiwgIlRSQkMyIiwgIlRSQUMiLCAiQ0Q0IiwgIkNEOEEiLCAiQ0Q4QiIpLAogICAgICAgICAgICAgICAgICAgICAgIGNoZW1va2luZS5yZWNlcHRvcnMgPSBjKCJDQ1I5IiwgIkNDUjciKSwKICAgICAgICAgICAgICAgICAgICAgICB0Y3IuYWN0aXZhdGlvbiA9IGMoIkNENSIsICJDRDI3IiksCiAgICAgICAgICAgICAgICAgICAgICAgcHJvbGlmZXJhdGlvbj1jKCJQQ05BIiwgIkNESzEiLCAiTUtJNjciKSwKICAgICAgICAgICAgICAgICAgICAgICBjeWNsaW4uRCA9IGMoIkNDTkQyIiwgIkNDTkQzIiksCiAgICAgICAgICAgICAgICAgICAgICAgcmVjb21iaW5hdGlvbj1jKCJSQUcxIiwgIlJBRzIiKSwKICAgICAgICAgICAgICAgICAgICAgICBhcG9wdG9zaXM9YygiSFJLIiwiQk1GIiwgIlRQNTNJTlAxIiksCiAgICAgICAgICAgICAgICAgICAgICAgc3RhZ2UubWFya2VycyA9IGMoIlNUMTgiLCAiSElWRVAzIiwgIlJHUEQzIiwgIlNNUEQzIiwgIkFRUDMiLCAiUk9SQyIsICJTQVRCMSIsICJUT1gyIikKICAgICAgICAgICAgICAgICAgICAgICApIAojIEZlYXR1cmVQbG90KHRjZWxscy5STkEucmVmLCBmZWF0dXJlcyA9IHQuY2VsbC5tYXJrZXJzJGtub3duLm1hcmtlcnMsIGNvbHMgPSB2aXJpZGlzOjp2aXJpZGlzKG49MTApKQpGZWF0dXJlUGxvdCh0Y2VsbHMuUk5BLnVuaW9uLCBmZWF0dXJlcyA9IHQuY2VsbC5tYXJrZXJzJGtub3duLm1hcmtlcnMsIGNvbHMgPSB2aXJpZGlzOjp2aXJpZGlzKG49MTApKQpgYGAKClZpc3VhbGl6ZSBUIGNlbGxzIGluIEFUQUMgZGF0YXNldDogaXMgdGhlIHRyYWplY3RvcnkgdmlzaWJsZSBpbiB0aGUgYmluYXJ5IG1hdHJpeD8KYGBge3J9CnRjZWxscy5BVEFDLnVuaW9uIDwtIHRjZWxscy5zZXUubGlzdCRBVEFDCiMgdGNlbGxzLkFUQUMudW5pb24gPC0gTm9ybWFsaXplRGF0YSh0Y2VsbHMuQVRBQy51bmlvbikKVmFyaWFibGVGZWF0dXJlcyh0Y2VsbHMuQVRBQy51bmlvbikgPC0gaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uCnRjZWxscy5BVEFDLnVuaW9uIDwtIFJ1bkxTSSh0Y2VsbHMuQVRBQy51bmlvbiwgbj01MCwgc2NhbGUubWF4ID0gTlVMTCkKdGNlbGxzLkFUQUMudW5pb24gPC0gUnVuVU1BUCh0Y2VsbHMuQVRBQy51bmlvbiwgcmVkdWN0aW9uID0gImxzaSIsIGRpbXMgPSAxOjUwKQoKRGltUGxvdCh0Y2VsbHMuQVRBQy51bmlvbiwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLCBsYWJlbCA9IFRSVUUpICsgZ2d0aXRsZSgiQVRBQyBnbWF0IikKYGBgCgojIyMjIFJ1biBDQ0EgYW5kIENvbm9zCgpSdW4gQ0NBCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NX0Kc2NlLmxpc3QgPC0gdGNlbGxzLnNjZS5saXN0CnJlZmVyZW5jZSA9ICJSTkEiCnF1ZXJ5ID0gIkFUQUMiIApzZXVyYXQubGlzdCA8LSBpbWFwKHNjZS5saXN0LCB+IGFzLlNldXJhdCgueCwgYXNzYXk9LnkpKQpzZXVyYXQubGlzdCA8LSBpbWFwKHNldXJhdC5saXN0LCB+IFJlbmFtZUNlbGxzKC54LCBhZGQuY2VsbC5pZD0ueSkpCiMjIFNjYWxlIGRhdGEKc2V1cmF0Lmxpc3QgPC0gbWFwKHNldXJhdC5saXN0LCB+IFNjYWxlRGF0YSgueCkpCiMjIENhbGN1bGF0ZSBDQ0EgYW5jaG9ycwp0cmFuc2Zlci5hbmNob3JzIDwtIEZpbmRUcmFuc2ZlckFuY2hvcnMocmVmZXJlbmNlID0gc2V1cmF0Lmxpc3RbW3JlZmVyZW5jZV1dLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1ZXJ5ID0gc2V1cmF0Lmxpc3RbW3F1ZXJ5XV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IGludGVncmF0ZV9mZWF0dXJlc191bmlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSAiY2NhIikKCiMjIEltcHV0ZSBleHByZXNzaW9uIHByb2ZpbGVzIGZvciBBVEFDIGNlbGxzIChmb3IgYWxsIGdlbmVzLCBub3QganVzdCBpbnRlZ3JhdGlvbiBmZWF0dXJlcykKcmVmZGF0YSA8LSBHZXRBc3NheURhdGEoc2V1cmF0Lmxpc3QkUk5BLCBhc3NheSA9ICJSTkEiLCBzbG90ID0gImRhdGEiKQppbXB1dGF0aW9uIDwtIFRyYW5zZmVyRGF0YShhbmNob3JzZXQgPSB0cmFuc2Zlci5hbmNob3JzLCByZWZkYXRhID0gcmVmZGF0YSwgd2VpZ2h0LnJlZHVjdGlvbiA9IHNldXJhdC5saXN0JEFUQUNbWyJMU0kiXV0pCgojIyBNZXJnZSBkYXRhc2V0cyBhbmQgY28tZW1iZWQKc2V1cmF0Lmxpc3QkQVRBQ1tbIlJOQSJdXSA8LSBpbXB1dGF0aW9uCmNvZW1iZWQgPC0gbWVyZ2UoeCA9IHNldXJhdC5saXN0JFJOQSwgeSA9IHNldXJhdC5saXN0JEFUQUMpCgpjb2VtYmVkIDwtIFNjYWxlRGF0YShjb2VtYmVkLCBmZWF0dXJlcyA9IGludGVncmF0ZV9mZWF0dXJlc191bmlvbiwgZG8uc2NhbGUgPSBGQUxTRSkKY29lbWJlZCA8LSBSdW5QQ0EoY29lbWJlZCwgZmVhdHVyZXMgPSBpbnRlZ3JhdGVfZmVhdHVyZXNfdW5pb24sIHZlcmJvc2UgPSBGQUxTRSkKY29lbWJlZCA8LSBSdW5VTUFQKGNvZW1iZWQsIGRpbXMgPSAxOjMwKQoKY29lbWJlZCA8LSBBZGRNZXRhRGF0YShjb2VtYmVkLCBtZXRhZGF0YSA9IGlmZWxzZShjb2xuYW1lcyhjb2VtYmVkKSAlaW4lIGNvbG5hbWVzKHNldXJhdC5saXN0W1tyZWZlcmVuY2VdXSksIHJlZmVyZW5jZSwgcXVlcnkpLCBjb2wubmFtZSA9ICJ0ZWNoIikKCkRpbVBsb3QoY29lbWJlZCwgZ3JvdXAuYnkgPSBjKCd0ZWNoJywgImFubm90YXRpb24iKSkKYGBgCgo8IS0tIFJ1biBDb25vcyAtLT4KPCEtLSBgYGB7cn0gLS0+CjwhLS0gZGF0YS5wcm9jZXNzZWQgPC0gbWFwKHNjZS5saXN0LCB+IGFzLlNldXJhdCgueCkpICAtLT4KPCEtLSBWYXJpYWJsZUZlYXR1cmVzKGRhdGEucHJvY2Vzc2VkW1tyZWZlcmVuY2VdXSkgPC0gaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uIC0tPgo8IS0tIFZhcmlhYmxlRmVhdHVyZXMoZGF0YS5wcm9jZXNzZWRbW3F1ZXJ5XV0pIDwtIGludGVncmF0ZV9mZWF0dXJlc191bmlvbiAtLT4KPCEtLSBkYXRhLnByb2Nlc3NlZCA8LSBtYXAoZGF0YS5wcm9jZXNzZWQsIH4gU2NhbGVEYXRhKC54KSAlPiUgUnVuUENBKGRpbXM9MTozMCkpIC0tPgo8IS0tIGwuY29uIDwtIENvbm9zJG5ldyhkYXRhLnByb2Nlc3NlZCxuLmNvcmVzPTMwKSAtLT4KPCEtLSBsLmNvbiRidWlsZEdyYXBoKGs9MTUsay5zZWxmPTUsay5zZWxmLndlaWdoPTAuMDEsbmNvbXBzPTMwLG4ub2RnZW5lcz01ZTMsc3BhY2U9J1BDQScpICAtLT4KCjwhLS0gbC5jb24kZmluZENvbW11bml0aWVzKHJlc29sdXRpb249MS41KSAtLT4KPCEtLSBsLmNvbiRlbWJlZEdyYXBoKGFscGhhPTEvMikgLS0+Cgo8IS0tIGNvbm9zLm91dCA8LSBjb25vcy5tb2RlbCRtb2RlbCAtLT4KPCEtLSBsLmNvbiRwbG90R3JhcGgoY29sb3IuYnkgPSAic2FtcGxlIikgLS0+Cgo8IS0tIGdlbmVYIDwtIHNldXJhdC5saXN0W1tyZWZlcmVuY2VdXUBhc3NheXMkUk5BQHNjYWxlLmRhdGFbMyxdIC0tPgo8IS0tIGdlbmVYIDwtIHNldE5hbWVzKGFubm90YXRpb25bLDFdLCByb3duYW1lcyhhbm5vdGF0aW9uKSkgLS0+CjwhLS0gbmV3LmxhYmVsLnByb2JhYmlsaXRpZXMgPC0gbC5jb24kcHJvcGFnYXRlTGFiZWxzKGxhYmVscyA9IGdlbmVYLCB2ZXJib3NlID0gVCwgZml4ZWQuaW5pdGlhbC5sYWJlbHM9VCkgLS0+CjwhLS0gaGlzdChuZXcubGFiZWwucHJvYmFiaWxpdGllcykgLS0+CjwhLS0gbC5jb24kY29ycmVjdEdlbmVzKGdlbmVzID0gaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uLCBjb3VudC5tYXRyaXggPSBNYXRyaXgoc2V1cmF0Lmxpc3QkQVRBQ0Bhc3NheXMkQVRBQ0BkYXRhKSkgLS0+Cgo8IS0tIGBgYCAtLT4KCmBgYHtyLCBmaWcuaGVpZ2h0PTIwLCBmaWcud2lkdGg9OX0KRmVhdHVyZVBsb3QoY29lbWJlZCwgZmVhdHVyZXMgPSB0LmNlbGwubWFya2VycyRzdGFnZS5tYXJrZXJzLCBzcGxpdC5ieSA9ICJ0ZWNoIiwgY29scyA9IHZpcmlkaXM6OnZpcmlkaXMobj0xMDApKSArIGdndGl0bGUoIlN0YWdlIE1hcmtlcnMiKQpgYGAKYGBge3IsIGZpZy5oZWlnaHQ9MjUsIGZpZy53aWR0aD05fQpGZWF0dXJlUGxvdChjb2VtYmVkLCBmZWF0dXJlcyA9IHQuY2VsbC5tYXJrZXJzJGtub3duLm1hcmtlcnMsIHNwbGl0LmJ5ID0gInRlY2giLCBzbG90ID0gImRhdGEiLCBjb2xzID0gdmlyaWRpczo6dmlyaWRpcyhuPTEwMCkpCmBgYApgYGB7cn0KRmVhdHVyZVBsb3QoY29lbWJlZCwgZmVhdHVyZXMgPSB0LmNlbGwubWFya2VycyRyZWNvbWJpbmF0aW9uLCBzcGxpdC5ieSA9ICJ0ZWNoIiwgc2xvdCA9ICJkYXRhIiwgY29scyA9IHZpcmlkaXM6OnZpcmlkaXMobj0xMDApKSAKYGBgCgpUcmFuc2ZlciBsYWJlbHMgb24gQVRBQyBkYXRhc2V0CmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NX0KY2VsbHR5cGUucHJlZGljdGlvbnMgPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IHRyYW5zZmVyLmFuY2hvcnMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVmZGF0YSA9IHNldXJhdC5saXN0W1tyZWZlcmVuY2VdXSRhbm5vdGF0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdlaWdodC5yZWR1Y3Rpb24gPSBzZXVyYXQubGlzdCRBVEFDW1siTFNJIl1dKQoKY29lbWJlZCA8LSBBZGRNZXRhRGF0YShjb2VtYmVkLCBtZXRhZGF0YSA9IGNlbGx0eXBlLnByZWRpY3Rpb25zKQpjb2VtYmVkQG1ldGEuZGF0YSAlPD4lCiAgcm93bmFtZXNfdG9fY29sdW1uKCkgJT4lCiAgZHBseXI6Om11dGF0ZShhbm5vdGF0aW9uPWlmZWxzZShpcy5uYShwcmVkaWN0ZWQuaWQpICwgYW5ub3RhdGlvbiwgTkEpKSAlPiUKICBjb2x1bW5fdG9fcm93bmFtZXMoKQoKY29lbWJlZEBtZXRhLmRhdGEgPC0KICBjb2VtYmVkQG1ldGEuZGF0YSAlPiUKICByb3duYW1lc190b19jb2x1bW4oKSAlPiUKICBkcGx5cjo6bXV0YXRlKGFubm90YXRpb249aWZlbHNlKGlzLm5hKGFubm90YXRpb24pICYgcHJlZGljdGlvbi5zY29yZS5tYXggPiAwLjUsIHByZWRpY3RlZC5pZCwgYW5ub3RhdGlvbikpICU+JQogIGNvbHVtbl90b19yb3duYW1lcygpCkNvbWJpbmVQbG90cygKICBsaXN0KERpbVBsb3QoY29lbWJlZCwgZ3JvdXAuYnkgPSBjKCJwcmVkaWN0ZWQuaWQiKSwgY29scyA9IGNlbGwudHlwZS5wYWwpICsgZ2d0aXRsZSgicHJlZGljdGlvbiIpLAogIERpbVBsb3QoY29lbWJlZCwgZ3JvdXAuYnkgPSBjKCJhbm5vdGF0aW9uIiksIGNvbHMgPSBjZWxsLnR5cGUucGFsKSArIGdndGl0bGUoIk9yaWdpbmFsICsgcHJlZGljdGlvbiIpKSwKICBsZWdlbmQgPSAidG9wIgogICkKYGBgCmBgYHtyfQpGZWF0dXJlUGxvdChjb2VtYmVkLCBmZWF0dXJlcyA9ICJwcmVkaWN0aW9uLnNjb3JlLm1heCIsIGNlbGxzID0gd2hpY2goY29lbWJlZCR0ZWNoPT0iQVRBQyIpKSArIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpCmBgYAoKCiMjIyBSdW4gUHNldWRvdGltZSBhbmFseXNpcyAKSWRlbnRpZnkgY2VsbCBvZiBvcmlnaW4gYmFzZWQgb24gSUdMTDEgYW5kIENEMzQKYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMH0KRmVhdHVyZVBsb3QoY29lbWJlZCwgZmVhdHVyZXMgPSBjKCJJR0xMMSIsICJDRDM0IiksIHNwbGl0LmJ5ID0gInRlY2giLCBzbG90ID0gImRhdGEiLCBjb2xzID0gdmlyaWRpczo6dmlyaWRpcyhuPTEwMCkpCmBgYApgYGB7cn0KY2VsbC5vbyA8LQogIGNvZW1iZWRAbWV0YS5kYXRhICU+JSAKICByb3duYW1lc190b19jb2x1bW4oImNlbGwiKSAlPiUKICBtdXRhdGUoSUdMTDE9Y29lbWJlZEBhc3NheXMkUk5BQGNvdW50c1siSUdMTDEiLGNlbGxdKSAlPiUKICBzZWxlY3QoY2VsbCwgYW5ub3RhdGlvbiwgSUdMTDEpICU+JQogIGFycmFuZ2UoLUlHTEwxKSAlPiUKICBmaWx0ZXIoYW5ub3RhdGlvbj09IkROIikgJT4lCiAgdG9wX24oMSwgSUdMTDEpICU+JQogIHB1bGwoY2VsbCkKCmNvZW1iZWRAcmVkdWN0aW9ucyR1bWFwQGNlbGwuZW1iZWRkaW5ncyAlPiUKICBhcy50aWJibGUocm93bmFtZXM9ImNlbGwiKSAlPiUKICBtdXRhdGUoY2VsbC5vbyA9IGlmZWxzZShjZWxsICVpbiUgY2VsbC5vbywgVCwgRikpICU+JQogIGdncGxvdChhZXMoVU1BUF8xLCBVTUFQXzIpKSArCiAgZ2VvbV9wb2ludChjb2xvcj0iZ3JleTUwIikgKwogIGdlb21fcG9pbnQoZGF0YT0uICU+JSBmaWx0ZXIoY2VsbC5vbyksY29sb3I9J3JlZCcpICsKICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoZGF0YT0uICU+JSBmaWx0ZXIoY2VsbC5vbyksIGFlcyhsYWJlbD0iY2VsbCBvZiBvcmlnaW4iKSwgY29sb3I9J3JlZCcpICsKICB0aGVtZV9jb3dwbG90KCkgCgpjb2VtYmVkIDwtIEFkZE1ldGFEYXRhKGNvZW1iZWQsIGlmZWxzZShjb2xuYW1lcyhjb2VtYmVkKT09Y2VsbC5vbywgVFJVRSwgRkFMU0UpLCBjb2wubmFtZSA9ICJpcm9vdF9jZWxsIikKCiAgCmBgYAoKCmBgYHtyfQptZXJnZWQuc2NlIDwtIFNpbmdsZUNlbGxFeHBlcmltZW50KGxpc3QoY291bnRzPWNvZW1iZWRAYXNzYXlzJFJOQUBjb3VudHMsIGxvZ2NvdW50cz1jb2VtYmVkQGFzc2F5cyRSTkFAZGF0YSksIGNvbERhdGE9Y29lbWJlZEBtZXRhLmRhdGFbLCBjKCJhbm5vdGF0aW9uIiwgInRlY2giLCAiaXJvb3RfY2VsbCIpXSwKICAgICAgICAgICAgICAgICAgICAgcmVkdWNlZERpbXMgPSBtYXAoY29lbWJlZEByZWR1Y3Rpb25zLCB+IC54QGNlbGwuZW1iZWRkaW5ncykpCgpzYXZlUkRTKG9iamVjdCA9IG1lcmdlZC5zY2UsICJ+L215X2RhdGEvVGNlbGxzX0NDQV9pbnRlZ3JhdGlvbl8yMDE5MTIwMy5SRFMiKQpzYXZlUkRTKG9iamVjdCA9IGludGVncmF0ZV9mZWF0dXJlc191bmlvbiwgIn4vbXlfZGF0YS9pbnRGZWF0dXJlc19UY2VsbHNfQ0NBX2ludGVncmF0aW9uXzIwMTkxMjAzLlJEUyIpCmBgYAoKSSBpbmZlciBwc2V1ZG90aW1lIHVzaW5nIHRoZSBkaWZmdXNpb24gcHNldWRvdGltZSBhbGdvcml0aG0gYXMgaW1wbGVtZW50ZWQgaW4gc2NhbnB5LiBNYWtpbmcgYW4gUi9yZXRpY3VsYXRlIHdyYXBwZXIgZm9yIHRoaXMgZnVuY3Rpb24gd291bGQgYmUgbmljZSwgYnV0IGZvciBub3csIHNlZSBgbXVsdGlPbWljX2JlbmNobWFyay9EUFRfdGNlbGxzLmlweW5iYC4KClJlYWQgc2NhbnB5IG91dHB1dCBhbmQgc2F2ZSBpbiBSIG9iamVjdC4KYGBge3J9CmRwdCA8LSByZWFkLmNzdignfi9teV9kYXRhL1RjZWxsc19DQ0FfaW50ZWdyYXRpb25fMjAxOTExMjdfc2NhbnB5X2RwdC5jc3YnKSAlPiUKICBzZWxlY3QoWCwgZHB0X3BzZXVkb3RpbWUpCgpjb2VtYmVkIDwtIEFkZE1ldGFEYXRhKGNvZW1iZWQsIGNvbHVtbl90b19yb3duYW1lcyhkcHQsICdYJykpCnNhdmVSRFMoY29lbWJlZCwgIn4vbXlfZGF0YS9UY2VsbHNfQ0NBX2ludGVncmF0aW9uX3NldXJhdF8yMDE5MTIwMy5SbWQiKQpjb2VtYmVkIDwtIHJlYWRSRFMoIn4vbXlfZGF0YS9UY2VsbHNfQ0NBX2ludGVncmF0aW9uX3NldXJhdF8yMDE5MTIwMy5SbWQiKQpgYGAKCgpWaXN1YWxpemUgcHNldWRvdGltZQoKYGBge3IsIGZpZy53aWR0aD0xMH0KRmVhdHVyZVBsb3QoY29lbWJlZCwgcmVkdWN0aW9uID0gInVtYXAiLCBmZWF0dXJlID0gImRwdF9wc2V1ZG90aW1lIiwgc3BsaXQuYnkgPSAidGVjaCIsIGNvbD12aXJpZGlzOjp2aXJpZGlzKDEwKSkgCmBgYAoKU2F2ZSBmaWd1cmUKYGBge3J9CkRpbVBsb3QoY29lbWJlZCwgZ3JvdXAuYnkgPSBjKCd0ZWNoJywgImFubm90YXRpb24iKSwgY29scyA9IGNlbGwudHlwZS5wYWwpCmBgYAoKCgpgYGB7ciwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTB9CmNvZW1iZWRAbWV0YS5kYXRhICU+JQogIGRwbHlyOjptdXRhdGUoYERQVCByYW5rYD1kZW5zZV9yYW5rKGRwdF9wc2V1ZG90aW1lKSkgJT4lCiAgZ2dwbG90KGFlcyhkcHRfcHNldWRvdGltZSkpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoZmlsbD1hbm5vdGF0aW9uKSwgYmlucz01MCkgKwogIGZhY2V0X2dyaWQoYW5ub3RhdGlvbn50ZWNoLCBzY2FsZXM9ImZyZWVfeSIpICsKICB0aGVtZV9idyhiYXNlX3NpemUgPSAxNikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNlbGwudHlwZS5wYWwpCgpgYGAKCkNoZWNrIGV4cHJlc3Npb24gb2YgbWFya2VycyBhbG9uZyBwc2V1ZG90aW1lCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NH0KY29lbWJlZEBhc3NheXMkUk5BQGRhdGFbdC5jZWxsLm1hcmtlcnMka25vd24ubWFya2VycywgXSAlPiUKICBhcy5tYXRyaXgoKSAlPiUKICByZXNoYXBlMjo6bWVsdCh2YXJuYW1lcz1jKCJnZW5lIiwgImNlbGwiKSkgJT4lCiAgZ3JvdXBfYnkoZ2VuZSkgJT4lCiAgIyBtdXRhdGUodmFsdWU9KHZhbHVlLW1lYW4odmFsdWUpKS9zZCh2YWx1ZSkpICU+JQogIGxlZnRfam9pbihjb2VtYmVkQG1ldGEuZGF0YVssImRwdF9wc2V1ZG90aW1lIiwgZHJvcD1GXSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJjZWxsIikpICU+JQogIG11dGF0ZShwc2V1ZG90aW1lLnJhbms9ZGVuc2VfcmFuayhkcHRfcHNldWRvdGltZSkpICU+JQogIGdncGxvdChhZXMocHNldWRvdGltZS5yYW5rLCBnZW5lKSkgKwogIGdlb21fdGlsZShhZXMoZmlsbD12YWx1ZSkpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfYygpCmBgYAoKQmluIHBzZXVkb3RpbWUgYW5kIHZpc3VhbGl6ZSBjZWxsIHR5cGUgY29tcG9zaXRpb24KCmBgYHtyLCBmaWcud2lkdGg9MTUsIGZpZy5oZWlnaHQ9NH0KZHB0LmRmIDwtIAogIGNvZW1iZWRAbWV0YS5kYXRhICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigiY2VsbCIpICU+JQogIGRwbHlyOjptdXRhdGUoZHB0X3Jhbms9ZGVuc2VfcmFuayhkcHRfcHNldWRvdGltZSkpICU+JQogIG11dGF0ZShkcHRfYmluPWN1dChkcHRfcmFuaywgYnJlYWtzID0gMTAwKSkgJT4lCiAgbXV0YXRlKGRwdF9iaW49YXMubnVtZXJpYyhkcHRfYmluKSkgJT4lCiAgc2VsZWN0KGNlbGwsdGVjaCwgYW5ub3RhdGlvbiwgcHJlZGljdGlvbi5zY29yZS5tYXgsIGRwdF9iaW4sIGRwdF9wc2V1ZG90aW1lLCBkcHRfcmFuaykKCmNlbGwudHlwZS5wbCA8LSBkcHQuZGYgJT4lCiAgZ2dwbG90KGFlcyhkcHRfYmluLCBmaWxsID0gYW5ub3RhdGlvbikpICsKICAjIGdlb21faGlzdG9ncmFtKGJpbnM9MTAwKSArCiAgZ2VvbV9iYXIoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNlbGwudHlwZS5wYWwsIG5hLnZhbHVlPSJncmV5NTAiKSArCiAgZmFjZXRfZ3JpZCh0ZWNofi4sIHNjYWxlcz0iZnJlZV95IikgKwogIHhsYWIoIlBzZXVkb3RpbWUgYmluIikgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE2KQoKY2VsbC50eXBlLnBsCmBgYAoKQ29ycmVsYXRpb24gYmV0d2VlbiBnbG9iYWwgYWNjZXNzaWJpbGl0eSBhbmQgcHNldWRvdGltZSBvcmRlcmluZy4KCmBgYHtyLCBmaWcud2lkdGg9MTUsIGZpZy5oZWlnaHQ9OH0Kc25hcC5vdXQgPC0gcmVhZFJEUyhmaWxlID0gIn4vbXlfZGF0YS9jZWxscmFuZ2VyLWF0YWMxMTBfY291bnRfMzA0MzlfV1NTUzgwMzgzNjBfR1JDaDM4LTFfMV8wLnNuYXBBVEFDLlJEUyIpCiMgYXRhYy5kcHQuZGYgPC0gCiMgICBjb2VtYmVkQG1ldGEuZGF0YSAlPiUKIyAgIHJvd25hbWVzX3RvX2NvbHVtbigiY2VsbCIpICU+JQojICAgZmlsdGVyKHRlY2g9PSJBVEFDIikgJT4lCiMgICAjIGdyb3VwX2J5KHRlY2gpICU+JQojICAgZHBseXI6Om11dGF0ZShkcHRfcmFuaz1kZW5zZV9yYW5rKGRwdF9wc2V1ZG90aW1lKSkgJT4lCiMgICBtdXRhdGUoZHB0X2Jpbj1jdXQoZHB0X3JhbmssIGJyZWFrcyA9IDEwMCkpICU+JQojICAgbXV0YXRlKGRwdF9iaW49YXMubnVtZXJpYyhkcHRfYmluKSkgJT4lCiMgICAjIHVuZ3JvdXAoKSAlPiUKIyAgIHNlbGVjdChjZWxsLHRlY2gsIGFubm90YXRpb24sIHByZWRpY3Rpb24uc2NvcmUubWF4LCBkcHRfYmluLCBkcHRfcHNldWRvdGltZSkKCmdyb3VwcyA8LSBkcHQuZGZbZHB0LmRmJHRlY2g9PSJBVEFDIiwgYygiY2VsbCIsICJkcHRfYmluIildCmJtYXQgPC0gc25hcC5vdXRAYm1hdFtzdHJfcmVtb3ZlKGdyb3VwcyRjZWxsLCAiQVRBQ18iKSxdCmZyYWMuYWNjZXNzaWJsZSA8LSByb3dTdW1zKGJtYXQpL25jb2woYm1hdCkKYWNjLmZyYWN0aW9uLnBsIDwtIGdyb3VwcyAlPiUKICBtdXRhdGUoZnJhY19hY2Nlc3NpYmxlPWZyYWMuYWNjZXNzaWJsZVtzdHJfcmVtb3ZlKGNlbGwsICJBVEFDXyIpXSkgJT4lCiAgZ2dwbG90KGFlcyhkcHRfYmluLCBmcmFjX2FjY2Vzc2libGUpKSArCiAgZ2VvbV9ib3hwbG90KGFlcyhncm91cD1hcy5mYWN0b3IoZHB0X2JpbikpLCBvdXRsaWVyLmFscGhhID0gMC4zKSArCiAgIyBnZW9tX2ppdHRlcihhbHBoYT0wLjEpICsKICB4bGFiKCJQc2V1ZG90aW1lIGJpbiIpICsKICB5bGFiKCJGcmFjdGlvbiBvZiBhY2Nlc3NpYmxlIGJpbnMiKSArCiAgZmFjZXRfZ3JpZCgnQVRBQyd+LikgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE2KSAKICAKcGxvdF9ncmlkKGNlbGwudHlwZS5wbCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0idG9wIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKSwgCiAgICAgICAgICBhY2MuZnJhY3Rpb24ucGwsIAogICAgICAgICAgYWxpZ24gPSAidiIsIG5jb2w9MSwgbnJvdz0yLCBheGlzPSJsIikKYGBgCgoKPCEtLSBWaXogbWFya2VycyAtLT4KPCEtLSBgYGB7cn0gLS0+CjwhLS0gYWNjLm1hdCA8LSBjb2VtYmVkQGFzc2F5cyRBVEFDQGRhdGEgLS0+CjwhLS0gbWFya2Vycy5hY2MgPC0gYWNjLm1hdFtpbnRlcnNlY3QoYyh0LmNlbGwubWFya2VycyRrbm93bi5tYXJrZXJzLCB0LmNlbGwubWFya2VycyRjaGVtb2tpbmUucmVjZXB0b3JzLCB0LmNlbGwubWFya2VycyRyZWNvbWJpbmF0aW9uKSwgcm93bmFtZXMoYWNjLm1hdCkpLCwgZHJvcD1GXSAtLT4KCjwhLS0gbWFya2Vycy5kZiA8LSBkYXRhLmZyYW1lKHQoYXMubWF0cml4KG1hcmtlcnMuYWNjWyxkcHQuZGYkY2VsbFtkcHQuZGYkdGVjaD09IkFUQUMiXV0pKSkgJT4lIC0tPgo8IS0tICAgcm93bmFtZXNfdG9fY29sdW1uKCJjZWxsIikgJT4lIC0tPgo8IS0tICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSByb3duYW1lcyhtYXJrZXJzLmFjYyksIG5hbWVzX3RvID0gIm1hcmtlci5nZW5lIiwgdmFsdWVzX3RvID0gImFjY2Vzc2liaWxpdHkiKSAtLT4KCjwhLS0gYW5ub3RhdGlvbi5obSA8LSBhdGFjLmRwdC5kZiAlPiUgLS0+CjwhLS0gICBncm91cF9ieShkcHRfYmluLCBhbm5vdGF0aW9uKSAlPiUgLS0+CjwhLS0gICBzdW1tYXJpc2Uobj1uKCkpICU+JSAtLT4KPCEtLSAgIGdncGxvdChhZXMoZHB0X2JpbiwgYW5ub3RhdGlvbikpICsgLS0+CjwhLS0gICBnZW9tX3RpbGUoYWVzKGFscGhhPW4sIGZpbGw9YW5ub3RhdGlvbikpICArIC0tPgo8IS0tICAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxNikgKyAtLT4KPCEtLSAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jZWxsLnR5cGUucGFsLCBuYS52YWx1ZT0iZ3JleTUwIikgKyAtLT4KPCEtLSAgIGd1aWRlcyhmaWxsPSdub25lJywgYWxwaGE9J25vbmUnKSArIC0tPgo8IS0tICAgdGhlbWUoYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKSAtLT4KCjwhLS0gbWFya2Vycy5obSA8LSBhdGFjLmRwdC5kZiAlPiUgLS0+CjwhLS0gICBmdWxsX2pvaW4obWFya2Vycy5kZikgJT4lIC0tPgo8IS0tICAgZ3JvdXBfYnkoZHB0X2JpbiwgbWFya2VyLmdlbmUpICU+JSAtLT4KPCEtLSAgIHN1bW1hcmlzZShmcmFjX2FjY2Vzc2libGU9c3VtKGFjY2Vzc2liaWxpdHkpL24oKSkgJT4lIC0tPgo8IS0tICAgdW5ncm91cCgpICU+JSAtLT4KPCEtLSAgIG11dGF0ZShtYXJrZXIuZ2VuZT1mYWN0b3IobWFya2VyLmdlbmUsIGxldmVscyA9IGMoIkNEMzQiLCAiSUdMTDEiLCAiVFJHQzIiLCAiVFJEQyIsICJQVENSQSIsICJUUkJDMiIsICJDQ1I5IiwiQ0NSNyIsICJSQUcxIiwgIlJBRzIiLCAiVFJBQyIsICJDRDQiLCAiQ0Q4QSIsICJDRDhCIikpKSAlPiUgLS0+CjwhLS0gICBtdXRhdGUobWFya2VyLmdlbmU9ZmFjdG9yKG1hcmtlci5nZW5lLCBsZXZlbHMgPSByZXYobGV2ZWxzKG1hcmtlci5nZW5lKSkpKSAlPiUgLS0+CjwhLS0gICBncm91cF9ieShtYXJrZXIuZ2VuZSkgJT4lIC0tPgo8IS0tICAgbXV0YXRlKGZyYWNfYWNjZXNzaWJsZT0oZnJhY19hY2Nlc3NpYmxlIC0gbWluKGZyYWNfYWNjZXNzaWJsZSkpL21heChmcmFjX2FjY2Vzc2libGUpIC0gbWluKGZyYWNfYWNjZXNzaWJsZSkpICU+JSAtLT4KPCEtLSAgIGdncGxvdChhZXMoZHB0X2JpbiwgbWFya2VyLmdlbmUsIGZpbGw9ZnJhY19hY2Nlc3NpYmxlKSkgKyAgLS0+CjwhLS0gICBnZW9tX3RpbGUoKSArIC0tPgo8IS0tICAgc2NhbGVfZmlsbF92aXJpZGlzX2MobmFtZT0iRnJhYy5jZWxscyIpICsgLS0+CjwhLS0gICB4bGFiKCJQc2V1ZG90aW1lIGJpbiIpICsgLS0+CjwhLS0gICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDE2KSArIC0tPgo8IS0tICAgdGhlbWUoYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkgLS0+Cgo8IS0tIGxlZyA8LSBnZXRfbGVnZW5kKG1hcmtlcnMuaG0pIC0tPgo8IS0tIGdyMSA8LSBwbG90X2dyaWQoYW5ub3RhdGlvbi5obSwgbWFya2Vycy5obSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksIG5yb3c9MiwgcmVsX2hlaWdodHMgPSBjKDEsMiksIGFsaWduID0gInYiKSAtLT4KPCEtLSBncjIgPC0gcGxvdF9ncmlkKGdncGxvdCgpICsgIHRoZW1lX3ZvaWQoKSxsZWcsIG5yb3c9MiwgcmVsX2hlaWdodHMgPSBjKDEsMikpIC0tPgo8IS0tIHBsb3RfZ3JpZChncjEsIGdyMiwgcmVsX3dpZHRocyA9IGMoMywxKSkgLS0+CjwhLS0gYGBgIC0tPgoKIyMgTW90aWYgYW5hbHlzaXMgCgpJIGluaXRpYWxseSB3YW50ZWQgdG8gY2FsbCBwZWFrcyBmcm9tIFNuYXBBVEFDIGNsdXN0ZXJzLCB0aGVuIGJ1aWxkIGEgY2VsbCB4IHBlYWsgbWF0cml4IG9uIHRob3NlIGRldGVjdGVkIHBlYWtzLCBidXQgU25hcEFUQUMvTUFDUzIgd2VyZSBkcml2aW4gbWUgbnV0cy4gCgo8IS0tIC0tLS0gLS0+CjwhLS0gKipUaGlzIGRvZXNuJ3Qgc2VlbSB0byB3b3JrKiogLS0+CjwhLS0gQ2FsbCBwZWFrcyAgLS0+CjwhLS0gYGBge3J9IC0tPgo8IS0tICMjIENhbGwgcGVha3Mgb24gY2x1c3RlcnMgLS0+CjwhLS0gY2x1c3RlcnMuc2VsIDwtIHVuaXF1ZSh0Y2VsbHMuc2NlLmF0YWMkc2V1cmF0X2NsdXN0ZXJzKSAtLT4KPCEtLSBwZWFrcy5scyA9IG1jbGFwcGx5KHNlcShjbHVzdGVycy5zZWwpLCBmdW5jdGlvbihpKXsgLS0+CjwhLS0gICBwcmludChwYXN0ZSgiY2x1c3RlciIsIGNsdXN0ZXJzLnNlbFtpXSkpIC0tPgo8IS0tICAgcGVha3MgPSBydW5NQUNTKCAtLT4KPCEtLSAgICAgICBvYmo9c25hcC5vdXRbd2hpY2goc25hcC5vdXRAbWV0YURhdGEkYmFyY29kZSAlaW4lIGNvbG5hbWVzKHRjZWxscy5zY2UuYXRhYylbdGNlbGxzLnNjZS5hdGFjJHNldXJhdF9jbHVzdGVycz09Y2x1c3RlcnMuc2VsW2ldXSksXSwgIC0tPgo8IS0tICAgICAgIG91dHB1dC5wcmVmaXg9cGFzdGUwKCJUY2VsbHNfRjc0X2NsdXN0ZXIiLCBjbHVzdGVycy5zZWxbaV0pLCAtLT4KPCEtLSAgICAgICBwYXRoLnRvLnNuYXB0b29scz0iL29wdC9jb25kYS9iaW4vc25hcHRvb2xzIiwgLS0+CjwhLS0gICAgICAgcGF0aC50by5tYWNzPSIvb3B0L2NvbmRhL2Jpbi9tYWNzMiIsIC0tPgo8IS0tICAgICAgIGdzaXplPSJocyIsICMgbW0sIGhzLCBldGMgLS0+CjwhLS0gICAgICAgYnVmZmVyLnNpemU9NTAwLCAgLS0+CjwhLS0gICAgICAgbnVtLmNvcmVzPTMsIC0tPgo8IS0tICAgICAgIG1hY3Mub3B0aW9ucz0iLS1ub21vZGVsIC0tc2hpZnQgMTAwIC0tZXh0IDIwMCAtLXF2YWwgNWUtMiAtQiAtLVNQTVIiLCAtLT4KPCEtLSAgICAgICB0bXAuZm9sZGVyPXRlbXBkaXIoKSAtLT4KPCEtLSAgKSAtLT4KPCEtLSBwZWFrcyAtLT4KPCEtLSB9LCBtYy5jb3Jlcz01KSAtLT4KCjwhLS0gcGVha3MubmFtZXMgPSBsaXN0LmZpbGVzKCJ+L215X2RhdGEvVGNlbGxzX3BlYWtzLyIsIHBhdHRlcm49Im5hcnJvd1BlYWsiLCBmdWxsLm5hbWVzID0gVCkgLS0+CjwhLS0gcGVhay5nci5scyA9IGxhcHBseShwZWFrcy5uYW1lcywgZnVuY3Rpb24oeCl7IC0tPgo8IS0tICAgcGVhay5kZiA9IHJlYWQudGFibGUoeCkgLS0+CjwhLS0gICBHUmFuZ2VzKHN0cl9yZW1vdmVfYWxsKHBlYWsuZGZbLDFdLCAiYid8JyIpLCBJUmFuZ2VzKHBlYWsuZGZbLDJdLCBwZWFrLmRmWywzXSkpIC0tPgo8IS0tIH0pIC0tPgo8IS0tIHBlYWsuZ3IgPSByZWR1Y2UoUmVkdWNlKGMsIHBlYWsuZ3IubHMpKSAtLT4KCjwhLS0gIyMgTWFrZSBjZWxsIGJ5IHBlYWsgbWF0cml4IChub3QgcnVuIGhlcmUpIC0tPgo8IS0tIHBlYWtzLmRmID0gYXMuZGF0YS5mcmFtZShwZWFrLmdyKVssMTozXTsgLS0+CjwhLS0gd3JpdGUudGFibGUocGVha3MuZGYsZmlsZSA9ICJ+L215X2RhdGEvVGNlbGxzX3BlYWtzL3BlYWtzLmNvbWJpbmVkLmJlZCIsYXBwZW5kPUZBTFNFLCAtLT4KPCEtLSAJCXF1b3RlPSBGQUxTRSxzZXA9Ilx0IiwgZW9sID0gIlxuIiwgbmEgPSAiTkEiLCBkZWMgPSAiLiIsICAtLT4KPCEtLSAJCXJvdy5uYW1lcyA9IEZBTFNFLCBjb2wubmFtZXMgPSBGQUxTRSwgcW1ldGhvZCA9IGMoImVzY2FwZSIsICJkb3VibGUiKSwgLS0+CjwhLS0gCQlmaWxlRW5jb2RpbmcgPSAiIikgLS0+CjwhLS0gYGBgIC0tPgoKPCEtLSBNYWtpbmcgY29tbW9uIHBlYWsgcmVmZXJlbmNlIHdpdGggc25hcHRvb2xzLiBJbiB0ZXJtaW5hbCAtLT4KPCEtLSBgYGAgLS0+CjwhLS0gc25hcHRvb2xzIHNuYXAtYWRkLXBtYXQgLS1zbmFwLWZpbGUgfi9teV9kYXRhL2NlbGxyYW5nZXItYXRhYzExMF9jb3VudF8zMDQzOV9XU1NTODAzODM2MF9HUkNoMzgtMV8xXzAuc25hcCAtLXBlYWstZmlsZSBwZWFrcy5jb21iaW5lZC5iZWQgIC0tPgo8IS0tIGBgYCAtLT4KCjwhLS0gQWRkIHBtYXQgdG8gc25hcCBvYmplY3QgLS0+CjwhLS0gYGBge3J9IC0tPgo8IS0tIHNuYXAub3V0IDwtIGNyZWF0ZVBtYXQoc25hcC5vdXQsIHBlYWsuZ3IsIGRvLnBhciA9IFQsIG51bS5jb3JlcyA9IDEwKSAtLT4KPCEtLSBgYGAgLS0+CjwhLS0gLS0tIC0tPgoKQWx0ZXJuYXRpdmU6IGxvYWQgcGVhayBtYXRyaXggZnJvbSBjZWxscmFuZ2VyIGFuZCBhZGQgdG8gc25hcCBvYmplY3QKYGBge3J9CmZpbHQucGVha3MgPC0gUmVhZDEwWF9oNSgifi9teV9kYXRhL2ZpbHRlcmVkX3BlYWtfYmNfbWF0cml4Lmg1IikKcGVha3MubWF0IDwtIHN0cl9zcGxpdChyb3duYW1lcyhmaWx0LnBlYWtzKSwgcGF0dGVybiA9ICI6fC0iKSAlPiUgbWFwKHJiaW5kKSAlPiUgcHVycnI6OnJlZHVjZShyYmluZCkKcGVha3MuZ3IgPC0gR1JhbmdlcyhwZWFrcy5tYXRbLDFdLCBJUmFuZ2VzKGFzLm51bWVyaWMocGVha3MubWF0WywyXSksIGFzLm51bWVyaWMocGVha3MubWF0WywzXSkpKQpzbmFwLnBtYXQgPC0gY3JlYXRlU25hcEZyb21QbWF0KG1hdD10KGZpbHQucGVha3NbLHNuYXAub3V0QGJhcmNvZGVdKSwgYmFyY29kZXM9c25hcC5vdXRAYmFyY29kZSwgcGVha3M9cGVha3MuZ3IpCnNuYXAucG1hdApgYGAKCkNhbGN1bGF0aW5nIGRldmlhdGlvbnMgaW4gVEYgYWNjZXNzaWJpbGl0eSB1c2luZyBDaHJvbVZBUi4gVGhpcyBpcyBhIG1lYXN1cmUgb2YgaG93IG11Y2ggaXMgbW90aWYgYWNjZXNzaWJpbGl0eSBpbiBlYWNoIGNlbGwgaXMgZW5yaWNoZWQgY29tcGFyZWQgdG8gYWxsIHRoZSBjZWxscyBhbmQgZ2VuZXJhbCBjZWxsIGNvdmVyYWdlLiBXaGlsZSBTbmFwQVRBQyBoYXMgYW4gd3JhcHBlciBhcm91bmQgQ2hyb21WQVIgdGhhdCBvdXRwdXRzIHRoZSBkZXZpYXRpb24gbWF0cml4LCBJIGp1c3QgdGFrZSB0aGUgY29kZSBmcm9tIHRoYXQgZnVuY3Rpb24gYW5kIHJ1biBldmVyeSBzdGVwIHNlcGFyYXRlbHkgdG8ga2VlcCB0aGUgdXNlZnVsIG91dHB1dHMgYW5kIHN0YXRpc3RpY3Mgb2YgY2hyb21WQVIuCgpgYGB7cn0Kc25hcC5wbWF0ID0gbWFrZUJpbmFyeShzbmFwLnBtYXQsICJwbWF0IikKCm9iaiA9IHNuYXAucG1hdAppbnB1dC5tYXQ9InBtYXQiCm1pbi5jb3VudD0xMApzcGVjaWVzPSJIb21vIHNhcGllbnMiCmdlbm9tZT1CU2dlbm9tZS5Ic2FwaWVucy5VQ1NDLmhnMzgKCmRhdGEudXNlID0gb2JqQHBtYXQKcGVhay51c2UgPSBvYmpAcGVhawoKbmNlbGwgPSBucm93KGRhdGEudXNlKQoKaWR5ID0gd2hpY2goTWF0cml4Ojpjb2xTdW1zKGRhdGEudXNlKSA+PSBtaW4uY291bnQpCmRhdGEudXNlID0gZGF0YS51c2VbLGlkeSxkcm9wcGluZz1UUlVFXQoJCnBlYWsudXNlID0gcGVhay51c2VbaWR5XQoKcnNlIDwtIFN1bW1hcml6ZWRFeHBlcmltZW50KAoJCWFzc2F5cyA9IGxpc3QoY291bnRzID0gdChkYXRhLnVzZSkpLCAKCQkJCSByb3dSYW5nZXMgPSBwZWFrLnVzZSwgCgkJCQkgY29sRGF0YSA9IERhdGFGcmFtZShDZWxsX1R5cGU9MTpucm93KGRhdGEudXNlKSwgZGVwdGg9TWF0cml4Ojpyb3dTdW1zKGRhdGEudXNlKSkKCSk7CnJzZSA8LSBhZGRHQ0JpYXMocnNlLCBnZW5vbWUgPSBnZW5vbWUpOwptb3RpZnMgPC0gZ2V0SmFzcGFyTW90aWZzKGNvbGxlY3Rpb24gPSAiQ09SRSIsIHNwZWNpZXM9c3BlY2llcyk7Cm1vdGlmX21tIDwtIG1hdGNoTW90aWZzKG1vdGlmcywgcnNlLCBnZW5vbWUgPSBnZW5vbWUpOwpkZXYgPC0gY29tcHV0ZURldmlhdGlvbnMob2JqZWN0ID0gcnNlLCBhbm5vdGF0aW9ucyA9IG1vdGlmX21tKTsKdmFyIDwtIGNvbXB1dGVWYXJpYWJpbGl0eShkZXYpCmBgYAoKU2F2ZQpgYGB7cn0Kcm93RGF0YShkZXYpICU8PiUKICBhcy50aWJibGUocm93bmFtZXM9Im1vdGlmIikgJT4lCiAgZnVsbF9qb2luKHZhcikgJT4lCiAgY29sdW1uX3RvX3Jvd25hbWVzKCdtb3RpZicpICU+JQogIERhdGFGcmFtZSgpCgpzYXZlUkRTKGRldiwgIn4vbXlfZGF0YS9UY2VsbHNfcGVha3MvVGNlbGxzX2Nocm9tVmFyT3V0cHV0LlJEUyIpICAKYGBgCgpWaXN1YWxpemUgbW90aWYgZW5yaWNobWVudApgYGB7ciwgZmlnLndpZHRoPTE1LCBmaWcuaGVpZ2h0PTEwfQpzYW1wbGVfZHB0X2JpbnMuZGYgPC0gYXRhYy5kcHQuZGYgJT4lCiAgbXV0YXRlKGNlbGw9c3RyX3JlbW92ZShjZWxsLCAiXkFUQUNfIikpICU+JQogIGFycmFuZ2UoZHB0X3BzZXVkb3RpbWUpCgptb3RpZi50b3B2YXIgPC0gdmFyICU+JSByb3duYW1lc190b19jb2x1bW4oIm1vdGlmIikgJT4lIHRvcF9uKDEwMCx2YXJpYWJpbGl0eSkgJT4lIHB1bGwobW90aWYpCm1tYXQudG9wdmFyIDwtIGRldkBhc3NheXMkZGF0YSR6W21vdGlmLnRvcHZhcixzYW1wbGVfZHB0X2JpbnMuZGYkY2VsbF0KCmFwcGx5KG1tYXQudG9wdmFyLCAxLCBmdW5jdGlvbih4KSB6b286OnJvbGxtZWFuKHgsIGs9MTApKSAlPiUgdCgpICU+JQogICMgbW1hdC50b3B2YXJbLHNhbXBsZV9kcHRfYmlucy5kZiRjZWxsXSAlPiUKICBwaGVhdG1hcDo6cGhlYXRtYXAoc2hvd19jb2xuYW1lcyA9IEYsIGNsdXN0ZXJfY29scyA9IEYsIGNsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9ICJjb3JyZWxhdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sID0gc2FtcGxlX2RwdF9iaW5zLmRmWyxjKCJjZWxsIiwgImFubm90YXRpb24iKV0gJT4lIGNvbHVtbl90b19yb3duYW1lcygiY2VsbCIpLCAKICAgICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBsaXN0KGFubm90YXRpb249Y2VsbC50eXBlLnBhbCksIGZvbnRzaXplID0gMTgsIGZvbnRzaXplX3JvdyA9IDE0CiAgICAgICAgICAgICAgICAgICAgICMgY29sb3IgPSBjb2xvclJhbXBQYWxldHRlKHJldihicmV3ZXIucGFsKG4gPSA3LCBuYW1lID0iU3BlY3RyYWwiKSkpKDEwMCkpCiAgICAgICAgICAgICAgICAgICAgICMgYnJlYWtzID0gc2VxKC0zLDMsMTAwKQogICkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9NH0KdGYubW90aWZzIDwtIHN0cl9zdWJzZXQocm93bmFtZXMoZGV2KSwgIkdBVEEiKQp0Zi5uYW1lcyA8LSAiSlVOQyIKcGxvdF9kcHRfbW90aWZzIDwtIGZ1bmN0aW9uKHRmLm5hbWVzLCB4X3Zhcj0iZHB0X2JpbiIpewogIHRmLm1vdGlmcyA8LSB2YXIgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigibW90aWYiKSAlPiUgZmlsdGVyKG5hbWUgJWluJSB0Zi5uYW1lcykgJT4lIHB1bGwobW90aWYpCiAgbW1hdC5kZiA8LSByZXNoYXBlMjo6bWVsdChkZXZAYXNzYXlzJGRhdGEkelt0Zi5tb3RpZnMsLCBkcm9wPUZdLCB2YXJuYW1lcz1jKCJtb3RpZiIsICJjZWxsIikpICU+JQogICAgbXV0YXRlKGNlbGw9cGFzdGUwKCJBVEFDXyIsIGNlbGwpLAogICAgICAgICAgIG1vdGlmPXN0cl9yZW1vdmUobW90aWYsICIuK18iKSkgJT4lCiAgICAjIG11dGF0ZSh4PXhfdmFyKSAlPiUKICAgIGxlZnRfam9pbihhdGFjLmRwdC5kZikgJT4lIAogICAgcmVuYW1lX2F0KHZhcnMoYyh4X3ZhcikpLGZ1bnMoYygieCIpKSkgCiAgbW1hdC5kZiAlPiUKICAgICAgZHJvcF9uYSh4KSAlPiUKICAgICAgZ2dwbG90KGFlcyh4LCB2YWx1ZSkpICsKICAgICAgZ2VvbV92aW9saW4oYWVzKGdyb3VwPXgpKSArCiAgICAgIGdlb21faml0dGVyKHNpemU9MC41KSArCiAgICAgICMgZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIpICsKICAgICAgZmFjZXRfd3JhcChtb3RpZn4uLCBzY2FsZT0iZnJlZV95IiwgbmNvbD0yKSAKICAgICAgIyBnZW9tX2JveHBsb3QoYWVzKGdyb3VwPWFzLmZhY3RvcihkcHRfYmluKSksIG91dGxpZXIuYWxwaGE9MC4xKQogIH0KCnBsb3RfZHB0X21vdGlmcyhjKCJUQlgxIiksIHhfdmFyPSJkcHRfYmluIikKYGBgCgpDb21wYXJlIG1vdGlmIGFjY2Vzc2liaWxpdHkgYW5kIFRGIGdlbmUgZXhwcmVzc2lvbiAKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMn0KdGYudG9wdmFyIDwtIG1vdGlmLnRvcHZhciAlPiUgc3RyX3JlbW92ZSgiLitfIikgCgpkcHQub3JkZXIgPC0KICBkcHQuZGYgJT4lCiAgYXJyYW5nZShkcHRfcHNldWRvdGltZSkgJT4lCiAgcHVsbCgpCgpnZW5leC5kZiA8LSBjb2VtYmVkQGFzc2F5cyRSTkFAZGF0YVt0Zi50b3B2YXJbd2hpY2godGYudG9wdmFyICVpbiUgcm93bmFtZXMoY29lbWJlZEBhc3NheXMkUk5BKSldLF0gJT4lCiAgYXMubWF0cml4KCkgJT4lCiAgcmVzaGFwZTI6Om1lbHQodmFybmFtZXM9YygiZ2VuZSIsICdjZWxsJykpICU+JQogIGZpbHRlcihzdHJfZGV0ZWN0KGNlbGwsICJSTkFfIikpICU+JQogIGxlZnRfam9pbihkcHQuZGYpICU+JQogIGFycmFuZ2UoZHB0X2JpbikgJT4lCiAgbXV0YXRlKHRlY2g9IlJOQSIpCgphY2Nlc3MuZGYgPC0gZGV2QGFzc2F5cyRkYXRhJHpbbW90aWYudG9wdmFyLF0gJT4lCiAgYXMubWF0cml4KCkgJT4lCiAgcmVzaGFwZTI6Om1lbHQodmFybmFtZXM9YygiZ2VuZSIsICdjZWxsJykpICU+JQogIG11dGF0ZShjZWxsPXN0cl9jKCJBVEFDXyIsIGNlbGwpLAogICAgICAgICBnZW5lPXN0cl9yZW1vdmUoZ2VuZSwgIi4rXyIpKSAlPiUKICBsZWZ0X2pvaW4oZHB0LmRmKSAlPiUKICAjIGFycmFuZ2UoZHB0X2JpbikgJT4lCiAgIyBncm91cF9ieShkcHRfYmluLCBnZW5lKSAlPiUKICAjIHN1bW1hcmlzZSh2YWx1ZT1tZWFuKHZhbHVlKSkgJT4lCiAgIyB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKHRlY2g9IkFUQUMiKQogIApiaW5kX3Jvd3MoYWNjZXNzLmRmLCBnZW5leC5kZikgJT4lCiAgZ3JvdXBfYnkodGVjaCwgZ2VuZSkgJT4lCiAgbXV0YXRlKHZhbHVlPXNjYWxlKHZhbHVlKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGdyb3VwX2J5KHRlY2gsZHB0X2JpbiwgZ2VuZSkgJT4lCiAgc3VtbWFyaXNlKHZhbHVlPW1lYW4odmFsdWUpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZ2dwbG90KGFlcyhkcHRfYmluLCBnZW5lLCBmaWxsPXZhbHVlKSkgKwogIGdlb21fdGlsZSgpICsKICBzY2FsZV9maWxsX2dyYWRpZW50MihtaWRwb2ludCA9IDApICsKICBmYWNldF93cmFwKHRlY2h+LikKCmBgYAoKYGBge3IsIGZpZy53aWR0aD0xNCwgZmlnLmhlaWdodD03fQpiaW5kX3Jvd3MoYWNjZXNzLmRmLCBnZW5leC5kZikgJT4lCiAgZmlsdGVyKGdlbmUgJWluJSBjKCJTUEkxIiwgIlJVTlgyIiwiUlVOWDMiLCAnVENGN0wyJywgIkUyRjQiKSkgJT4lCiAgICBmaWx0ZXIoIXN0cl9kZXRlY3QoY2VsbCwgIl5BVEFDXyIpIHwgdGVjaD09IkFUQUMiKSAlPiUKICAjIGdyb3VwX2J5KHRlY2gsIGdlbmUpICU+JQogICMgbXV0YXRlKHZhbHVlPXNjYWxlKHZhbHVlKSkgJT4lCiAgIyB1bmdyb3VwKCkgJT4lCiAgIyBkcm9wX25hKCkgJT4lCiMgZmlsdGVyKHRlY2g9PSJSTkEiKSAlPiUKICBkcm9wX25hKGRwdF9iaW4pICU+JQogIGdncGxvdChhZXMoZHB0X2JpbiwgdmFsdWUpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3I9YW5ub3RhdGlvbiksIGFscGhhPTAuMikgKwogIGZhY2V0X2dyaWQodGVjaH5nZW5lLCBzY2FsZXMgPSAiZnJlZV95IikgKwogIGdlb21fc21vb3RoKCkKCgpgYGAKCmBgYHtyLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9N30KYmluZF9yb3dzKGFjY2Vzcy5kZiwgZ2VuZXguZGYpICU+JQogIGZpbHRlcihnZW5lICVpbiUgYygiRUxLMyIsICJKVU5CIiwgIkZPUyIpKSAlPiUKICBmaWx0ZXIoIXN0cl9kZXRlY3QoY2VsbCwgIl5BVEFDXyIpIHwgdGVjaD09IkFUQUMiKSAlPiUKICAjIGdyb3VwX2J5KHRlY2gsIGdlbmUpICU+JQogICMgbXV0YXRlKHZhbHVlPXNjYWxlKHZhbHVlKSkgJT4lCiAgIyB1bmdyb3VwKCkgJT4lCiAgZHJvcF9uYShkcHRfYmluKSAlPiUKICBnZ3Bsb3QoYWVzKGRwdF9iaW4sIHZhbHVlKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yPWFubm90YXRpb24pLCBhbHBoYT0wLjIpICsKICBmYWNldF9ncmlkKHRlY2h+Z2VuZSwgc2NhbGVzID0gImZyZWVfeSIpICsKICBnZW9tX3Ntb290aCgpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWNlbGwudHlwZS5wYWwpCgoKYGBgCgojIyBQc2V1ZG90aW1lIGxhZyBiZXR3ZWVuIERQKFEpIGluIGFjY2Vzc2liaWxpdHkgYW5kIGdlbmUgZXhwcmVzc2lvbgpUaGUgRFAgKFEpIGNsdXN0ZXIgaW4gdGhlIEFUQUMgY2VsbHMgaXMgc2NvcmVkIHdpdGggaGlnaCBjb25maWRlbmNlCmBgYHtyfQpkcHEuY29lbWJlZCA8LSBjb2VtYmVkWyx3aGljaChjb2VtYmVkJGFubm90YXRpb249PSJEUCAoUSkiKV0KCkZlYXR1cmVQbG90KGNvZW1iZWQsIGZlYXR1cmU9InByZWRpY3Rpb24uc2NvcmUubWF4IikKRGltUGxvdChjb2VtYmVkLCBncm91cC5ieSA9ImFubm90YXRpb24iLCBzcGxpdC5ieSA9ICJ0ZWNoIikKCmBgYAoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD02fQpjY2Eub2JqIDwtIHRyYW5zZmVyLmFuY2hvcnNAb2JqZWN0Lmxpc3RbWzFdXQpuZXcubWV0YWRhdGEgPC0gY29lbWJlZEBtZXRhLmRhdGFbLGMoImFubm90YXRpb24iLCAidGVjaCIpLCBkcm9wPUZdICU+JSByb3duYW1lc190b19jb2x1bW4oKSAlPiUKICBtdXRhdGUocm93bmFtZT1pZmVsc2Uoc3RyX2RldGVjdChyb3duYW1lLCAiXlJOQSIpLCBzdHJfYyhyb3duYW1lLCAiX3JlZmVyZW5jZSIpLCBzdHJfYyhyb3duYW1lLCAiX3F1ZXJ5IikpKSAlPiUKICBjb2x1bW5fdG9fcm93bmFtZXMoKQpjY2Eub2JqIDwtIEFkZE1ldGFEYXRhKGNjYS5vYmosIG5ldy5tZXRhZGF0YSkKY2NhLm9iakBtZXRhLmRhdGEKRGltUGxvdChjY2Eub2JqLCBncm91cC5ieT1jKCJhbm5vdGF0aW9uIiwidGVjaCIpLCByZWR1Y3Rpb24gPSAiY2NhIiwgZGltcyA9IDE6MikKRGltUGxvdChjY2Eub2JqLCBncm91cC5ieT1jKCJhbm5vdGF0aW9uIiwidGVjaCIpLCByZWR1Y3Rpb24gPSAiY2NhIiwgZGltcyA9IDM6NCkKRGltUGxvdChjY2Eub2JqLCBncm91cC5ieT1jKCJhbm5vdGF0aW9uIiwidGVjaCIpLCByZWR1Y3Rpb24gPSAiY2NhIiwgZGltcyA9IDU6NikKYGBgCgpgYGB7ciwgZmlnLmhlaWdodD0xOCwgZmlnLndpZHRoPTE4fQp0b3AuY2MuZ2VuZXMgPC0gY2NhLm9iakByZWR1Y3Rpb25zJGNjYS5sMkBmZWF0dXJlLmxvYWRpbmdzICU+JSAKICByZXNoYXBlMjo6bWVsdCh2YXJuYW1lcz1jKCJnZW5lIiwgIkNDIikpICU+JQogIGdyb3VwX2J5KENDKSAlPiUKICBtdXRhdGUocmFuaz1yYW5rKGFicyh2YWx1ZSkpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZmlsdGVyKHJhbmsgPiAobWF4KHJhbmspLTEwKSkgJT4lCiAgcHVsbChnZW5lKSAlPiUKICB1bmlxdWUoKQogIAphdGFjLm1hdCA8LSBjb2VtYmVkQGFzc2F5cyRBVEFDQGRhdGEKcm5hLm1hdCA8LSBjb2VtYmVkQGFzc2F5cyRSTkFAZGF0YQoKYXRhYy5tYXRbZ2VuZS5vaSxdICU+JQogIHsuWyx3aGljaChhcHBseSguLDIsIGZ1bmN0aW9uKHgpIHN1bSh4KSE9MCkpXX0gJT4lCiAgcGhlYXRtYXA6OnBoZWF0bWFwKHNob3dfY29sbmFtZXM9RiwgY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gImNvcnJlbGF0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sID0gY29lbWJlZEBtZXRhLmRhdGFbLCJhbm5vdGF0aW9uIiwgZHJvcD1GXQogICAgICAgICAgICAgICAgICAgICApCmBgYApgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQpkcHEuY2VsbHMgPC0gcm93bmFtZXMobmV3Lm1ldGFkYXRhW25ldy5tZXRhZGF0YSRhbm5vdGF0aW9uPT0iRFAgKFEpIixdKQpkcHEucXVlcnkuaXggPC0gd2hpY2godHJhbnNmZXIuYW5jaG9yc0BxdWVyeS5jZWxscyAlaW4lIGRwcS5jZWxscykKZHBxLnJlZi5peCA8LSB3aGljaCh0cmFuc2Zlci5hbmNob3JzQHJlZmVyZW5jZS5jZWxscyAlaW4lIGRwcS5jZWxscykKbmV3Lm1ldGFkYXRhICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigpICU+JQogIGZpbHRlcih0ZWNoPT0iQVRBQyIpCnRyYW5zZmVyLmFuY2hvcnNAYW5jaG9ycyAlPiUKICBhcy50aWJibGUoKSAlPiUKICBtdXRhdGUoY2VsbDE9dHJhbnNmZXIuYW5jaG9yc0ByZWZlcmVuY2UuY2VsbHNbY2VsbDFdKSAlPiUKICBtdXRhdGUoY2VsbDI9dHJhbnNmZXIuYW5jaG9yc0BxdWVyeS5jZWxsc1tjZWxsMl0pICU+JQogIG11dGF0ZShhbm5vLmNlbGwxID0gbmV3Lm1ldGFkYXRhW2NlbGwxLCAnYW5ub3RhdGlvbiddKSAlPiUKICBtdXRhdGUoYW5uby5jZWxsMiA9IG5ldy5tZXRhZGF0YVtjZWxsMiwgJ2Fubm90YXRpb24nXSkgJT4lCiAgIyBzcHJlYWQoY2VsbDIsIHNjb3JlKSAKICBnZ3Bsb3QoYWVzKHNjb3JlKSkgKwogIGdlb21faGlzdG9ncmFtKCkgKwogIHhsaW0oMCwxKSArCiAgIyBnZW9tX3RpbGUoKSArCiAgZmFjZXRfZ3JpZChhbm5vLmNlbGwxfmFubm8uY2VsbDIsIHNjYWxlcz0iZnJlZV95Iiwgc3BhY2U9ImZyZWUiLCBsYWJlbGxlciA9ICJsYWJlbF9ib3RoIikgCmBgYAoKPCEtLSBzaG93IGFuY2hvciBtYXQgLS0+Cgo8IS0tIGBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9IC0tPgo8IS0tIGFuY2hvci5tYXQgPC0gdHJhbnNmZXIuYW5jaG9yc0BhbmNob3JzICU+JSAtLT4KPCEtLSAgIGFzLnRpYmJsZSgpICU+JSAtLT4KPCEtLSAgIG11dGF0ZShjZWxsMT10cmFuc2Zlci5hbmNob3JzQHJlZmVyZW5jZS5jZWxsc1tjZWxsMV0pICU+JSAtLT4KPCEtLSAgIG11dGF0ZShjZWxsMj10cmFuc2Zlci5hbmNob3JzQHF1ZXJ5LmNlbGxzW2NlbGwyXSkgJT4lIC0tPgo8IS0tICAgc3ByZWFkKGNlbGwyLCBzY29yZSkgJT4lIC0tPgo8IS0tICAgY29sdW1uX3RvX3Jvd25hbWVzKCdjZWxsMScpICU+JSAtLT4KPCEtLSAgIGFzLm1hdHJpeCgpIC0tPgoKPCEtLSBhbmNob3IubWF0ICU+JSAgLS0+CjwhLS0gICBpZmVsc2UoaXMubmEoLiksIDAsIC4pICU+JSAtLT4KPCEtLSAgIHBoZWF0bWFwOjpwaGVhdG1hcChzaG93X3Jvd25hbWVzID0gRiwgc2hvd19jb2xuYW1lcyA9IEYsIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sID0gbmV3Lm1ldGFkYXRhWywnYW5ub3RhdGlvbicsIGRyb3A9Rl0sIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fcm93ID0gbmV3Lm1ldGFkYXRhWywnYW5ub3RhdGlvbicsIGRyb3A9Rl0sIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fY29sb3JzID0gbGlzdChhbm5vdGF0aW9uPWNlbGwudHlwZS5wYWwpKSAtLT4KPCEtLSBgYGAgLS0+CgpgYGB7cn0KcHJlZC5zY29yZXMgPC0gY29sbmFtZXMoY29lbWJlZEBtZXRhLmRhdGEpICU+JSBzdHJfc3Vic2V0KCJwcmVkaWN0aW9uLnNjb3JlIikgCmNvZW1iZWRAbWV0YS5kYXRhICU+JQogIGZpbHRlcih0ZWNoPT0iQVRBQyIpICU+JQogIHNlbGVjdChjKCJhbm5vdGF0aW9uIiwgcHJlZC5zY29yZXMpKSAlPiUKICBwaXZvdF9sb25nZXIoY29scz0tYW5ub3RhdGlvbiwgbmFtZXNfdG8gPSAiY2xhc3MiKSAlPiUKICBtdXRhdGUoY2xhc3M9c3RyX3JlbW92ZShjbGFzcywgInByZWRpY3Rpb24uc2NvcmUuIikpICU+JQogIGZpbHRlcihjbGFzcyE9Im1heCIpICU+JQogIGdncGxvdChhZXMoYW5ub3RhdGlvbiwgdmFsdWUsIGZpbGw9Y2xhc3MpKSArCiAgZ2VvbV9ib3hwbG90KCkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTEwfQpEZWZhdWx0QXNzYXkoZHBxLmNvZW1iZWQpIDwtICJSTkEiCmRwcS5jb2VtYmVkIDwtIFNjYWxlRGF0YShkcHEuY29lbWJlZCwgZmVhdHVyZXMgPSBpbnRlZ3JhdGVfZmVhdHVyZXNfdW5pb24pCmRwcS5jb2VtYmVkIDwtIFJ1blBDQShkcHEuY29lbWJlZCwgZmVhdHVyZXMgPSBpbnRlZ3JhdGVfZmVhdHVyZXNfdW5pb24pCgpwbG90X2dyaWQoCiAgRGltUGxvdChkcHEuY29lbWJlZCwgZ3JvdXAuYnkgPSBjKCJ0ZWNoIiksIHJlZHVjdGlvbiA9ICJwY2EiKSAsCiAgRmVhdHVyZVBsb3QoZHBxLmNvZW1iZWQsIGZlYXR1cmU9ImRwdF9wc2V1ZG90aW1lIiwgcmVkdWN0aW9uID0gInBjYSIpICsgc2NhbGVfY29sb3JfdmlyaWRpc19jKCkgCiAgKQpgYGAKYGBge3J9CnRvcFBDMS5kZiA8LSBkcHEuY29lbWJlZEByZWR1Y3Rpb25zJHBjYUBmZWF0dXJlLmxvYWRpbmdzWywxLCBkcm9wPUZdICU+JQogIGFzLnRpYmJsZShyb3duYW1lcz0iZ2VuZSIpICU+JQogICMgYXJyYW5nZShQQ18xKQogIG11dGF0ZShyYW5rPSBkZW5zZV9yYW5rKFBDXzEpKSAlPiUKICBtdXRhdGUobGFiZWw9aWZlbHNlKHJhbmsgPiAobigpLTEwKSB8IHJhbmsgPCAoMTApICwgZ2VuZSxOQSkpIAoKdG9wUEMxLmRmICU+JQogIGdncGxvdChhZXMocmFuaywgUENfMSkpICsKICBnZW9tX3BvaW50KCkrCiAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKGRhdGE9LiAlPiUgZmlsdGVyKCFpcy5uYShsYWJlbCkpLCBhZXMobGFiZWw9bGFiZWwpKQpgYGAKYGBge3IsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTh9CkZlYXR1cmVQbG90KGRwcS5jb2VtYmVkLCBmZWF0dXJlPXVuaXF1ZSh0b3BQQzEuZGYkbGFiZWwpWzE6Nl0sIHJlZHVjdGlvbiA9ICJ1bWFwIikgCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKCkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTR9CmNjYS5vYmpAcmVkdWN0aW9ucyRjY2FAZmVhdHVyZS5sb2FkaW5ncyAlPiUKICByZXNoYXBlMgogIHBoZWF0bWFwOjpwaGVhdG1hcCgpCkRpbUhlYXRtYXAoY2NhLm9iaiwgcmVkdWN0aW9uID0gImNjYSIsIGFzc2F5cyA9ICJBVEFDIikKRmVhdHVyZVBsb3QoY2NhLm9iaiwgcmVkdWN0aW9uPSJjY2EiLCBmZWF0dXJlPWdlbmUub2kpCgpgYGAKCgo8IS0tIERpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBiZXR3ZWVuIFJOQSBhbmQgQVRBQyBEUChRKSBjZWxscyAtLT4KPCEtLSBgYGB7cn0gLS0+CjwhLS0gVmFyaWFibGVGZWF0dXJlcyhjb2VtYmVkKSA8LSBpbnRlZ3JhdGVfZmVhdHVyZXNfdW5pb24gLS0+CjwhLS0gZHBxLmRpZmYgPC0gRmluZE1hcmtlcnMoZHBxLmNvZW1iZWQsIGZlYXR1cmVzPVZhcmlhYmxlRmVhdHVyZXMoY29lbWJlZCksICAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJ0ZWNoIiwgaWRlbnQuMSA9ICJSTkEiLCBpZGVudC4yID0gIkFUQUMiKSAtLT4KCjwhLS0gdG9wLmRpZmYgPC0gcm93bmFtZXMoZHBxLmRpZmZbMToxMCxdKSAtLT4KPCEtLSBGZWF0dXJlUGxvdChjb2VtYmVkLCBmZWF0dXJlcyA9IHRvcC5kaWZmWzE6M10sIHNwbGl0LmJ5ID0gInRlY2giLCBjb2xzID0gdmlyaWRpczo6dmlyaWRpcyhuPTEwMCksIHNsb3QgPSAic2NhbGUuZGF0YSIsIG1heC5jdXRvZmYgPSAxMCkgKyBnZ3RpdGxlKCJTdGFnZSBNYXJrZXJzIikgLS0+CjwhLS0gYGBgIC0tPgo8IS0tIGBgYHtyfSAtLT4KCjwhLS0gVmxuUGxvdChjb2VtYmVkWywgY29lbWJlZCR0ZWNoPT0iUk5BIl0sIGZlYXR1cmVzID0gdG9wLmRpZmZbMV0sIGdyb3VwLmJ5ID0gImFubm90YXRpb24iLCBwdC5zaXplID0gMC4xLCBzcGxpdC5ieSA9ICd0ZWNoJykgLS0+CjwhLS0gVmxuUGxvdChjb2VtYmVkLCBmZWF0dXJlcyA9IGMoIkFRUDMiLCAiVFJCQzIiKSwgZ3JvdXAuYnkgPSAidGVjaCIpIC0tPgo8IS0tIGBgYCAtLT4KCmBgYHtyLCBmaWcud2lkdGg9MTJ9CmF0YWMubWF0IDwtIGNvZW1iZWRAYXNzYXlzJEFUQUNAZGF0YQpybmEubWF0IDwtIGNvZW1iZWRAYXNzYXlzJFJOQUBkYXRhCgpnZW5lLm9pIDwtIHVuaXF1ZSh0b3BQQzEuZGYkbGFiZWwpICU+JSBzdHJfc3Vic2V0KCJUUkFWIikKZ2VuZS5vaQphdGFjLmRmIDwtIGF0YWMubWF0W2dlbmUub2ksLCBkcm9wPUZdICU+JQogIGFzLm1hdHJpeCgpICU+JQogIHJlc2hhcGUyOjptZWx0KHZhcm5hbWVzPWMoImdlbmUiLCAiY2VsbCIpKSAlPiUKICBtdXRhdGUodGVjaD0iQVRBQyIpICU+JQogIGxlZnRfam9pbihkcHQuZGYsIGJ5PWMoJ2NlbGwnLCAidGVjaCIpKSAKIyAlPiUKIyAgIGdyb3VwX2J5KHRlY2gsIGRwdF9iaW4sIGdlbmUpICU+JQojICAgc3VtbWFyaXNlKGZyYWM9c3VtKHZhbHVlIT0wKS9uKCkpCgpybmEuZGYgPC0gcm5hLm1hdFtnZW5lLm9pLCwgZHJvcD1GXSAlPiUKICBhcy5tYXRyaXgoKSAlPiUKICByZXNoYXBlMjo6bWVsdCh2YXJuYW1lcz1jKCJnZW5lIiwgImNlbGwiKSkgJT4lCiAgZmlsdGVyKHN0cl9kZXRlY3QoY2VsbCwgIlJOQV8iKSkgJT4lCiAgbXV0YXRlKHRlY2g9IlJOQSIpICU+JQogIGxlZnRfam9pbihkcHQuZGYsIGJ5PWMoJ2NlbGwnLCAidGVjaCIpKQoKCmFjYy5wbCA8LSBhdGFjLmRmICU+JQogIGRyb3BfbmEoZHB0X2JpbikgJT4lCiAgZ2dwbG90KGFlcyhkcHRfYmluLCBmaWxsPWFzLmZhY3Rvcih2YWx1ZSkpKSArCiAgZ2VvbV9iYXIoKSAKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Y2VsbC50eXBlLnBhbCwgbmEudmFsdWU9ImdyZXk1MCIpCmFjYy5wbApleC5wbCA8LSBybmEuZGYgJT4lCiAgZmlsdGVyKGdlbmU9PWdlbmUub2kpICU+JQogICMgbXV0YXRlKHZhbHVlPWlmZWxzZSh2YWx1ZT4wLDEsMCkpICU+JQogICMgZmlsdGVyKHZhbHVlIT0wKSAlPiUKICBnZ3Bsb3QoYWVzKGRwdF9iaW4sIHZhbHVlKSkgKwogICMgZ2VvbV92aW9saW4oYWxwaGE9MC4yKSArCiAgIyBnZW9tX3BvaW50KCkKICAjIHNjYWxlX2ZpbGxfdmlyaWRpc19kKCkKICAjIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jZWxsLnR5cGUucGFsKQogIGdlb21faml0dGVyKGFscGhhPTAuNSwgc2l6ZT0wLjUpICsKICBnZW9tX3Ntb290aCgpIAogIHhsaW0oMCw1MCkKCgpwbG90X2dyaWQoYWNjLnBsLCBleC5wbCwgbmNvbD0xLCBucm93PTIsIGFsaWduID0gInYiLCBheGlzLj0ibCIpCmBgYAoKYGBge3J9CmF0YWMuZGYgJT4lCiAgZHJvcF9uYShkcHRfcHNldWRvdGltZSkgJT4lCiAgZ3JvdXBfYnkoIGdlbmUsIGRwdF9iaW4pICU+JQogICMgc3VtbWFyaXNlKGZyYWM9c3VtKHZhbHVlKS9uKCkpICU+JQogIGdncGxvdChhZXMoZHB0X2JpbiwgdmFsdWUsIGNvbG9yPWdlbmUpKSArCiAgIyBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKCkKYGBgCgotLS0KCgoKCgoKCg==